2024-06-29 19:25:17 +02:00
|
|
|
#include "QuickStartUtils.h"
|
2022-02-02 19:57:14 +01:00
|
|
|
#include "BootUtils.h"
|
2023-04-02 11:59:16 +02:00
|
|
|
#include "MenuUtils.h"
|
2022-01-16 01:04:43 +01:00
|
|
|
#include "logger.h"
|
|
|
|
|
|
|
|
#include <coreinit/exit.h>
|
|
|
|
#include <coreinit/foreground.h>
|
2022-02-02 19:57:14 +01:00
|
|
|
#include <coreinit/memdefaultheap.h>
|
|
|
|
#include <coreinit/thread.h>
|
2022-01-16 01:04:43 +01:00
|
|
|
#include <nn/acp/title.h>
|
|
|
|
#include <nn/act/client_cpp.h>
|
|
|
|
#include <nn/ccr/sys_caffeine.h>
|
|
|
|
#include <nn/sl.h>
|
2024-05-05 17:37:02 +02:00
|
|
|
#include <nsysccr/cdc.h>
|
2024-04-25 17:20:27 +02:00
|
|
|
#include <optional>
|
2022-01-16 01:04:43 +01:00
|
|
|
#include <proc_ui/procui.h>
|
2024-04-25 17:19:53 +02:00
|
|
|
#include <rpxloader/rpxloader.h>
|
2022-02-02 19:57:14 +01:00
|
|
|
#include <sysapp/launch.h>
|
|
|
|
#include <sysapp/title.h>
|
2022-01-16 01:04:43 +01:00
|
|
|
|
|
|
|
extern "C" void __fini_wut();
|
2024-05-05 17:37:02 +02:00
|
|
|
extern "C" void CCRSysCaffeineBootCheckAbort();
|
2022-01-16 01:04:43 +01:00
|
|
|
|
2024-04-25 17:19:53 +02:00
|
|
|
#define UPPER_TITLE_ID_HOMEBREW 0x0005000F
|
|
|
|
#define TITLE_ID_HOMEBREW_MASK (((uint64_t) UPPER_TITLE_ID_HOMEBREW) << 32)
|
|
|
|
|
2022-01-16 01:04:43 +01:00
|
|
|
static void StartAppletAndExit() {
|
|
|
|
DEBUG_FUNCTION_LINE("Wait for applet");
|
|
|
|
ProcUIInit(OSSavesDone_ReadyToRelease);
|
|
|
|
|
2022-02-03 14:01:11 +01:00
|
|
|
bool doProcUi = true;
|
2022-01-16 01:04:43 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-04-25 17:20:27 +02:00
|
|
|
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{};
|
|
|
|
};
|
|
|
|
|
2024-05-05 17:37:02 +02:00
|
|
|
class QuickStartAutoAbort {
|
|
|
|
public:
|
|
|
|
QuickStartAutoAbort() {
|
2024-06-29 19:25:17 +02:00
|
|
|
OSCreateAlarm(&mDRCConnectedAlarm);
|
2024-05-05 17:37:02 +02:00
|
|
|
OSSetPeriodicAlarm(&mDRCConnectedAlarm,
|
2024-06-29 19:25:17 +02:00
|
|
|
OSGetTime() + OSSecondsToTicks(10),
|
2024-05-05 17:37:02 +02:00
|
|
|
OSSecondsToTicks(1),
|
|
|
|
&AbortOnDRCDisconnect);
|
2024-06-29 19:25:17 +02:00
|
|
|
OSCreateAlarm(&mAlarm);
|
2024-05-05 17:37:02 +02:00
|
|
|
OSSetAlarm(&mAlarm, OSSecondsToTicks(120), AbortQuickStartTitle);
|
|
|
|
mDRCConnected = IsDRCConnected();
|
|
|
|
}
|
|
|
|
~QuickStartAutoAbort() {
|
|
|
|
OSCancelAlarm(&mDRCConnectedAlarm);
|
|
|
|
OSCancelAlarm(&mAlarm);
|
|
|
|
|
2024-06-29 19:25:17 +02:00
|
|
|
OSWaitAlarm(&mDRCConnectedAlarm);
|
|
|
|
OSWaitAlarm(&mAlarm);
|
|
|
|
|
2024-05-05 17:37:02 +02:00
|
|
|
// Reconnect the DRC if it was connected at launch but then disconnected;
|
|
|
|
if (mDRCConnected && !IsDRCConnected()) {
|
2024-06-05 18:51:33 +02:00
|
|
|
DEBUG_FUNCTION_LINE("Wake up GamePad");
|
2024-05-05 17:37:02 +02:00
|
|
|
CCRCDCWowlWakeDrcArg args = {.state = 1};
|
|
|
|
CCRCDCWowlWakeDrc(&args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsDRCConnected() {
|
|
|
|
CCRCDCDrcState state = {};
|
2024-06-29 19:25:17 +02:00
|
|
|
if (CCRCDCSysGetDrcState(CCR_CDC_DESTINATION_DRC0, &state) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2024-05-05 17:37:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void AbortQuickStartTitle(OSAlarm *alarm, OSContext *) {
|
2024-06-29 19:25:17 +02:00
|
|
|
DEBUG_FUNCTION_LINE_INFO("GamePad was disconnected, lets abort the quick start menu");
|
2024-05-05 17:37:02 +02:00
|
|
|
CCRSysCaffeineBootCheckAbort();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AbortOnDRCDisconnect(OSAlarm *alarm, OSContext *) {
|
|
|
|
if (!IsDRCConnected()) {
|
2024-06-05 18:51:33 +02:00
|
|
|
// The gamepad does reconnect after selecting a title, make sure it's still disconnected after waiting 3 seconds
|
|
|
|
OSSleepTicks(OSSecondsToTicks(3));
|
|
|
|
if (!IsDRCConnected()) {
|
|
|
|
DEBUG_FUNCTION_LINE("GamePad was disconnected, lets abort the quick start menu");
|
|
|
|
CCRSysCaffeineBootCheckAbort();
|
|
|
|
}
|
2024-05-05 17:37:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
OSAlarm mDRCConnectedAlarm{};
|
|
|
|
OSAlarm mAlarm{};
|
|
|
|
bool mDRCConnected = false;
|
|
|
|
};
|
|
|
|
|
2024-04-26 10:32:27 +02:00
|
|
|
bool launchQuickStartTitle() {
|
2024-05-05 17:37:02 +02:00
|
|
|
// Automatically abort quick start if selecting takes longer than 120 seconds or the DRC disconnects
|
|
|
|
QuickStartAutoAbort quickStartAutoAbort;
|
|
|
|
|
|
|
|
// Waits until the quick start menu has been closed.
|
2022-01-16 01:04:43 +01:00
|
|
|
auto bootCheck = CCRSysCaffeineBootCheck();
|
|
|
|
if (bootCheck == 0) {
|
|
|
|
nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap);
|
|
|
|
char path[0x80];
|
2022-02-03 13:03:38 +01:00
|
|
|
nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000); // ECO process
|
2024-04-25 17:20:27 +02:00
|
|
|
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;
|
|
|
|
}
|
2022-02-04 21:42:53 +01:00
|
|
|
}
|
2022-01-16 01:04:43 +01:00
|
|
|
|
2022-02-03 13:03:38 +01:00
|
|
|
CCRAppLaunchParam data; // load sys caffeine data
|
2022-01-16 01:04:43 +01:00
|
|
|
// load app launch param
|
|
|
|
CCRSysCaffeineGetAppLaunchParam(&data);
|
|
|
|
|
2024-05-05 17:25:33 +02:00
|
|
|
if (data.launchInfoDatabaseEntryId == 1) { // This id is hardcoded into the nn_sl.rpl
|
2024-05-05 02:43:10 +02:00
|
|
|
DEBUG_FUNCTION_LINE("Launch Quick Start Settings");
|
|
|
|
SysAppSettingsArgs args{};
|
2024-05-05 17:25:33 +02:00
|
|
|
args.jumpTo = SYS_SETTINGS_JUMP_TO_QUICK_START_SETTINGS; // quick start settings
|
2024-05-05 02:43:10 +02:00
|
|
|
_SYSLaunchSettings(&args);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-31 14:37:34 +02:00
|
|
|
loadConsoleAccount(data.uuid);
|
|
|
|
|
2024-05-05 17:25:33 +02:00
|
|
|
auto result = launchInfoDatabase.GetLaunchInfoById(&info, data.launchInfoDatabaseEntryId);
|
2022-01-16 01:04:43 +01:00
|
|
|
|
|
|
|
nn::sl::Finalize();
|
|
|
|
|
|
|
|
if (!result.IsSuccess()) {
|
|
|
|
DEBUG_FUNCTION_LINE("GetLaunchInfoById failed.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-04-25 17:19:53 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-01-16 01:04:43 +01:00
|
|
|
if (info.titleId == 0x0005001010040000L ||
|
|
|
|
info.titleId == 0x0005001010040100L ||
|
|
|
|
info.titleId == 0x0005001010040200L) {
|
|
|
|
DEBUG_FUNCTION_LINE("Skip quick starting into the Wii U Menu");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-04-25 17:18:51 +02:00
|
|
|
if (info.titleId == 0x0005001010047000L ||
|
|
|
|
info.titleId == 0x0005001010047100L ||
|
|
|
|
info.titleId == 0x0005001010047200L) {
|
2024-05-05 02:43:10 +02:00
|
|
|
DEBUG_FUNCTION_LINE("Launch System Settings");
|
|
|
|
_SYSLaunchSettings(nullptr);
|
2024-04-25 17:18:51 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-01-16 01:04:43 +01:00
|
|
|
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);
|
2022-07-25 11:52:33 +02:00
|
|
|
_SYSSwitchTo(SYSAPP_PFID_DOWNLOAD_MANAGEMENT);
|
2022-01-16 01:04:43 +01:00
|
|
|
|
|
|
|
StartAppletAndExit();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (info.titleId == 0x000500301001600AL ||
|
|
|
|
info.titleId == 0x000500301001610AL ||
|
|
|
|
info.titleId == 0x000500301001620AL) {
|
|
|
|
DEBUG_FUNCTION_LINE("Launching Miiverse");
|
|
|
|
loadConsoleAccount(data.uuid);
|
2022-07-25 11:52:33 +02:00
|
|
|
_SYSSwitchTo(SYSAPP_PFID_MIIVERSE);
|
2022-01-16 01:04:43 +01:00
|
|
|
|
|
|
|
StartAppletAndExit();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (info.titleId == 0x000500301001500AL ||
|
|
|
|
info.titleId == 0x000500301001510AL ||
|
|
|
|
info.titleId == 0x000500301001520AL) {
|
|
|
|
DEBUG_FUNCTION_LINE("Launching Friendlist");
|
|
|
|
loadConsoleAccount(data.uuid);
|
2022-07-25 11:52:33 +02:00
|
|
|
_SYSSwitchTo(SYSAPP_PFID_FRIENDLIST);
|
2022-01-16 01:04:43 +01:00
|
|
|
|
|
|
|
StartAppletAndExit();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (info.titleId == 0x000500301001300AL ||
|
|
|
|
info.titleId == 0x000500301001310AL ||
|
|
|
|
info.titleId == 0x000500301001320AL) {
|
|
|
|
DEBUG_FUNCTION_LINE("Launching TVii");
|
|
|
|
loadConsoleAccount(data.uuid);
|
2022-07-25 11:52:33 +02:00
|
|
|
_SYSSwitchTo(SYSAPP_PFID_TVII);
|
2022-01-16 01:04:43 +01:00
|
|
|
|
|
|
|
StartAppletAndExit();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-02-03 13:03:38 +01:00
|
|
|
if (info.titleId == 0x0005001010004000L) { // OSv0
|
2022-01-18 16:19:04 +01:00
|
|
|
DEBUG_FUNCTION_LINE("Launching vWii System Menu");
|
|
|
|
bootvWiiMenu();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-04-02 11:59:16 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2022-01-16 01:04:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MCPTitleListType titleInfo;
|
|
|
|
int32_t handle = MCP_Open();
|
2023-04-02 11:59:16 +02:00
|
|
|
auto err = MCP_GetTitleInfo(handle, titleIdToLaunch, &titleInfo);
|
2022-01-16 01:04:43 +01:00
|
|
|
MCP_Close(handle);
|
|
|
|
if (err == 0) {
|
2023-04-02 11:59:16 +02:00
|
|
|
DEBUG_FUNCTION_LINE("Launch %016llX", titleIdToLaunch);
|
2022-01-16 01:04:43 +01:00
|
|
|
ACPAssignTitlePatch(&titleInfo);
|
2023-04-02 11:59:16 +02:00
|
|
|
_SYSLaunchTitleWithStdArgsInNoSplash(titleIdToLaunch, nullptr);
|
2022-01-16 01:04:43 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-04-02 11:59:16 +02:00
|
|
|
DEBUG_FUNCTION_LINE("Launch Wii U Menu!");
|
2022-01-16 01:04:43 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
DEBUG_FUNCTION_LINE("No quick start");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|