AutobootModule/source/QuickStartUtils.cpp
2024-04-25 17:20:27 +02:00

297 lines
9.9 KiB
C++

#include <malloc.h>
#include "BootUtils.h"
#include "MenuUtils.h"
#include "QuickStartUtils.h"
#include "logger.h"
#include <coreinit/exit.h>
#include <coreinit/foreground.h>
#include <coreinit/memdefaultheap.h>
#include <coreinit/thread.h>
#include <nn/acp/title.h>
#include <nn/act/client_cpp.h>
#include <nn/ccr/sys_caffeine.h>
#include <nn/sl.h>
#include <optional>
#include <proc_ui/procui.h>
#include <rpxloader/rpxloader.h>
#include <sysapp/launch.h>
#include <sysapp/title.h>
extern "C" void __fini_wut();
#define UPPER_TITLE_ID_HOMEBREW 0x0005000F
#define TITLE_ID_HOMEBREW_MASK (((uint64_t) UPPER_TITLE_ID_HOMEBREW) << 32)
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);
}
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();
}
class FileStreamWrapper {
public:
static std::unique_ptr<FileStreamWrapper> CreateFromPath(std::string_view path, std::string_view mode = "r") {
return std::unique_ptr<FileStreamWrapper>(new FileStreamWrapper(path, mode));
}
~FileStreamWrapper() {
mFileStream.reset();
FSDelClient(&mFsClient, FS_ERROR_FLAG_NONE);
}
nn::sl::details::IStreamBase &GetStream() {
return *mFileStream;
}
private:
explicit FileStreamWrapper(std::string_view path, std::string_view mode) {
FSAddClient(&mFsClient, FS_ERROR_FLAG_NONE);
FSInitCmdBlock(&mCmdBlock);
mFileStream = std::make_unique<nn::sl::FileStream>();
mFileStream->Initialize(&mFsClient, &mCmdBlock, path.data(), mode.data());
}
std::unique_ptr<nn::sl::FileStream> mFileStream{};
FSClient mFsClient{};
FSCmdBlock mCmdBlock{};
};
bool getQuickBoot() {
// Waits until the quick start menu has been closed. TODO: abort this checks after a given time?
auto bootCheck = CCRSysCaffeineBootCheck();
if (bootCheck == 0) {
nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap);
char path[0x80];
nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000); // ECO process
nn::sl::LaunchInfoDatabase launchInfoDatabase;
nn::sl::LaunchInfo info;
{
// In theory the region doesn't even matter.
// The region is to load a "system table" into the LaunchInfoDatabase which provides the LaunchInfos for
// the Wii U Menu and System Settings. In the code below we check for all possible System Settings title id and
// have a fallback to the Wii U Menu... This means we could get away a wrong region, but let's use the correct one
// anyway
const auto region = []() {
if (SYSCheckTitleExists(0x0005001010047000L)) { // JPN System Settings
return nn::sl::REGION_JPN;
} else if (SYSCheckTitleExists(0x0005001010047100L)) { // USA System Settings
return nn::sl::REGION_USA;
} else if (SYSCheckTitleExists(0x0005001010047200L)) { // EUR System Settings
return nn::sl::REGION_EUR;
}
return nn::sl::REGION_EUR;
}();
auto fileStream = FileStreamWrapper::CreateFromPath(path);
if (launchInfoDatabase.Load(fileStream->GetStream(), region).IsFailure()) {
DEBUG_FUNCTION_LINE_ERR("Failed to load LaunchInfoDatabase");
return false;
}
}
CCRAppLaunchParam data; // load sys caffeine data
// load app launch param
CCRSysCaffeineGetAppLaunchParam(&data);
loadConsoleAccount(data.uuid);
auto result = launchInfoDatabase.GetLaunchInfoById(&info, data.titleId);
nn::sl::Finalize();
if (!result.IsSuccess()) {
DEBUG_FUNCTION_LINE("GetLaunchInfoById failed.");
return false;
}
if ((info.titleId & TITLE_ID_HOMEBREW_MASK) == TITLE_ID_HOMEBREW_MASK) {
std::string homebrewPath = info.parameter;
DEBUG_FUNCTION_LINE("Trying to launch homebrew title: \"%s\"", homebrewPath.c_str());
if (auto err = RPXLoader_LaunchHomebrew(homebrewPath.c_str()); err != RPX_LOADER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to launch homebrew title: %s (%d)", RPXLoader_GetStatusStr(err), err);
return false;
}
return true;
}
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 == 0x0005001010047000L ||
info.titleId == 0x0005001010047100L ||
info.titleId == 0x0005001010047200L) {
DEBUG_FUNCTION_LINE("Launch Settings");
_SYSLaunchSettings(nullptr);
return true;
}
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(SYSAPP_PFID_DOWNLOAD_MANAGEMENT);
StartAppletAndExit();
return true;
}
if (info.titleId == 0x000500301001600AL ||
info.titleId == 0x000500301001610AL ||
info.titleId == 0x000500301001620AL) {
DEBUG_FUNCTION_LINE("Launching Miiverse");
loadConsoleAccount(data.uuid);
_SYSSwitchTo(SYSAPP_PFID_MIIVERSE);
StartAppletAndExit();
return true;
}
if (info.titleId == 0x000500301001500AL ||
info.titleId == 0x000500301001510AL ||
info.titleId == 0x000500301001520AL) {
DEBUG_FUNCTION_LINE("Launching Friendlist");
loadConsoleAccount(data.uuid);
_SYSSwitchTo(SYSAPP_PFID_FRIENDLIST);
StartAppletAndExit();
return true;
}
if (info.titleId == 0x000500301001300AL ||
info.titleId == 0x000500301001310AL ||
info.titleId == 0x000500301001320AL) {
DEBUG_FUNCTION_LINE("Launching TVii");
loadConsoleAccount(data.uuid);
_SYSSwitchTo(SYSAPP_PFID_TVII);
StartAppletAndExit();
return true;
}
if (info.titleId == 0x0005001010004000L) { // OSv0
DEBUG_FUNCTION_LINE("Launching vWii System Menu");
bootvWiiMenu();
return true;
}
uint64_t titleIdToLaunch = info.titleId;
switch (info.mediaType) {
case nn::sl::NN_SL_MEDIA_TYPE_ODD: {
if (!handleDiscInsertScreen(titleIdToLaunch, &titleIdToLaunch)) {
DEBUG_FUNCTION_LINE("Launch Wii U Menu!");
return false;
}
break;
}
default: {
if (!SYSCheckTitleExists(titleIdToLaunch)) {
DEBUG_FUNCTION_LINE("Title %016llX doesn't exist", titleIdToLaunch);
return false;
}
}
}
MCPTitleListType titleInfo;
int32_t handle = MCP_Open();
auto err = MCP_GetTitleInfo(handle, titleIdToLaunch, &titleInfo);
MCP_Close(handle);
if (err == 0) {
DEBUG_FUNCTION_LINE("Launch %016llX", titleIdToLaunch);
ACPAssignTitlePatch(&titleInfo);
_SYSLaunchTitleWithStdArgsInNoSplash(titleIdToLaunch, nullptr);
return true;
}
DEBUG_FUNCTION_LINE("Launch Wii U Menu!");
return false;
} else {
DEBUG_FUNCTION_LINE("No quick start");
}
return false;
}