mirror of
https://github.com/wiiu-env/AutobootModule.git
synced 2024-06-01 05:38:45 +02:00
Compare commits
23 Commits
AutobootMo
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
7c987b5b9d | ||
|
8f495d213f | ||
|
713f2a3aed | ||
|
e6cfed9da8 | ||
|
d8b2f36cf0 | ||
|
083998faab | ||
|
b5c007d921 | ||
|
b2ae87ae1f | ||
|
67dfdd1fc8 | ||
|
4cc08d82e2 | ||
|
f80e83dff9 | ||
|
b245a71cdb | ||
|
688b834d2a | ||
|
bf8d1a974d | ||
|
f313152874 | ||
|
f1a240ddbc | ||
|
ed1e612602 | ||
|
011ab8bcf1 | ||
|
1830addd61 | ||
|
6f7061044a | ||
|
6954a7f9f5 | ||
|
4e821e4fa2 | ||
|
10161be0a2 |
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "docker"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
||||||
clang-format:
|
clang-format:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: clang-format
|
- name: clang-format
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
||||||
|
@ -17,7 +17,7 @@ jobs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format
|
needs: clang-format
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: create version.h
|
- name: create version.h
|
||||||
run: |
|
run: |
|
||||||
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
||||||
|
@ -48,7 +48,7 @@ jobs:
|
||||||
- name: zip artifact
|
- name: zip artifact
|
||||||
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.rpx
|
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.rpx
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: "softprops/action-gh-release@v1"
|
uses: "softprops/action-gh-release@v2"
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||||
draft: false
|
draft: false
|
||||||
|
|
6
.github/workflows/pr.yml
vendored
6
.github/workflows/pr.yml
vendored
|
@ -6,7 +6,7 @@ jobs:
|
||||||
clang-format:
|
clang-format:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: clang-format
|
- name: clang-format
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
||||||
|
@ -14,7 +14,7 @@ jobs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format
|
needs: clang-format
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: build binary with logging
|
- name: build binary with logging
|
||||||
run: |
|
run: |
|
||||||
docker build . -t builder
|
docker build . -t builder
|
||||||
|
@ -25,7 +25,7 @@ jobs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format
|
needs: clang-format
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: create version.h
|
- name: create version.h
|
||||||
run: |
|
run: |
|
||||||
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")
|
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,5 +2,7 @@
|
||||||
*.rpx
|
*.rpx
|
||||||
build/
|
build/
|
||||||
.idea/
|
.idea/
|
||||||
|
.vscode/
|
||||||
cmake-build-debug/
|
cmake-build-debug/
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
*.zip
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
FROM ghcr.io/wiiu-env/devkitppc:20230402
|
FROM ghcr.io/wiiu-env/devkitppc:20240505
|
||||||
|
|
||||||
COPY --from=ghcr.io/wiiu-env/libmocha:20220919 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/libmocha:20230621 /artifacts $DEVKITPRO
|
||||||
|
COPY --from=ghcr.io/wiiu-env/librpxloader:20240425 /artifacts $DEVKITPRO
|
||||||
|
|
||||||
WORKDIR project
|
WORKDIR project
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -10,6 +10,8 @@ TOPDIR ?= $(CURDIR)
|
||||||
|
|
||||||
include $(DEVKITPRO)/wut/share/wut_rules
|
include $(DEVKITPRO)/wut/share/wut_rules
|
||||||
|
|
||||||
|
WUMS_ROOT := $(DEVKITPRO)/wums
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# TARGET is the name of the output
|
# TARGET is the name of the output
|
||||||
# BUILD is the directory where object files & intermediate files will be placed
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
@ -36,7 +38,7 @@ CXXFLAGS := $(CFLAGS) -std=c++20 -fno-rtti
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) --entry=_start -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -g $(ARCH) $(RPXSPECS) --entry=_start -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := -lpng -lmocha -lwut -lz
|
LIBS := -lrpxloader -lpng -lmocha -lwut -lz
|
||||||
|
|
||||||
ifeq ($(DEBUG),1)
|
ifeq ($(DEBUG),1)
|
||||||
CXXFLAGS += -DDEBUG -g
|
CXXFLAGS += -DDEBUG -g
|
||||||
|
@ -52,7 +54,7 @@ endif
|
||||||
# list of directories containing libraries, this must be the top level
|
# list of directories containing libraries, this must be the top level
|
||||||
# containing include and lib
|
# containing include and lib
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUT_ROOT)/usr
|
LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUT_ROOT) $(WUT_ROOT)/usr
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# no real need to edit anything past this point unless you need to add additional
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
|
BIN
data/icon.png
BIN
data/icon.png
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
139
source/InputUtils.cpp
Normal file
139
source/InputUtils.cpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#include "InputUtils.h"
|
||||||
|
#include <coreinit/thread.h>
|
||||||
|
#include <padscore/kpad.h>
|
||||||
|
#include <padscore/wpad.h>
|
||||||
|
#include <vpad/input.h>
|
||||||
|
|
||||||
|
uint32_t remapWiiMoteButtons(uint32_t buttons) {
|
||||||
|
uint32_t convButtons = 0;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_LEFT)
|
||||||
|
convButtons |= VPAD_BUTTON_LEFT;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_RIGHT)
|
||||||
|
convButtons |= VPAD_BUTTON_RIGHT;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_DOWN)
|
||||||
|
convButtons |= VPAD_BUTTON_DOWN;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_UP)
|
||||||
|
convButtons |= VPAD_BUTTON_UP;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_PLUS)
|
||||||
|
convButtons |= VPAD_BUTTON_PLUS;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_2)
|
||||||
|
convButtons |= VPAD_BUTTON_Y;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_1)
|
||||||
|
convButtons |= VPAD_BUTTON_X;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_B)
|
||||||
|
convButtons |= VPAD_BUTTON_B;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_A)
|
||||||
|
convButtons |= VPAD_BUTTON_A;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_MINUS)
|
||||||
|
convButtons |= VPAD_BUTTON_MINUS;
|
||||||
|
|
||||||
|
if (buttons & WPAD_BUTTON_HOME)
|
||||||
|
convButtons |= VPAD_BUTTON_HOME;
|
||||||
|
|
||||||
|
return convButtons;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t remapClassicButtons(uint32_t buttons) {
|
||||||
|
uint32_t convButtons = 0;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_LEFT)
|
||||||
|
convButtons |= VPAD_BUTTON_LEFT;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_RIGHT)
|
||||||
|
convButtons |= VPAD_BUTTON_RIGHT;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_DOWN)
|
||||||
|
convButtons |= VPAD_BUTTON_DOWN;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_UP)
|
||||||
|
convButtons |= VPAD_BUTTON_UP;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_PLUS)
|
||||||
|
convButtons |= VPAD_BUTTON_PLUS;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_X)
|
||||||
|
convButtons |= VPAD_BUTTON_X;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_Y)
|
||||||
|
convButtons |= VPAD_BUTTON_Y;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_B)
|
||||||
|
convButtons |= VPAD_BUTTON_B;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_A)
|
||||||
|
convButtons |= VPAD_BUTTON_A;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_MINUS)
|
||||||
|
convButtons |= VPAD_BUTTON_MINUS;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_HOME)
|
||||||
|
convButtons |= VPAD_BUTTON_HOME;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_ZR)
|
||||||
|
convButtons |= VPAD_BUTTON_ZR;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_ZL)
|
||||||
|
convButtons |= VPAD_BUTTON_ZL;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_R)
|
||||||
|
convButtons |= VPAD_BUTTON_R;
|
||||||
|
|
||||||
|
if (buttons & WPAD_CLASSIC_BUTTON_L)
|
||||||
|
convButtons |= VPAD_BUTTON_L;
|
||||||
|
|
||||||
|
return convButtons;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputUtils::InputData InputUtils::getControllerInput() {
|
||||||
|
InputData inputData{};
|
||||||
|
VPADStatus vpadStatus{};
|
||||||
|
VPADReadError vpadError = VPAD_READ_UNINITIALIZED;
|
||||||
|
do {
|
||||||
|
if (VPADRead(VPAD_CHAN_0, &vpadStatus, 1, &vpadError) > 0 && vpadError == VPAD_READ_SUCCESS) {
|
||||||
|
inputData.trigger = vpadStatus.trigger;
|
||||||
|
inputData.hold = vpadStatus.hold;
|
||||||
|
inputData.release = vpadStatus.release;
|
||||||
|
} else {
|
||||||
|
OSSleepTicks(OSMillisecondsToTicks(1));
|
||||||
|
}
|
||||||
|
} while (vpadError == VPAD_READ_NO_SAMPLES);
|
||||||
|
|
||||||
|
KPADStatus kpadStatus{};
|
||||||
|
KPADError kpadError = KPAD_ERROR_UNINITIALIZED;
|
||||||
|
for (int32_t i = 0; i < 4; i++) {
|
||||||
|
if (KPADReadEx((KPADChan) i, &kpadStatus, 1, &kpadError) > 0) {
|
||||||
|
if (kpadError == KPAD_ERROR_OK && kpadStatus.extensionType != 0xFF) {
|
||||||
|
if (kpadStatus.extensionType == WPAD_EXT_CORE || kpadStatus.extensionType == WPAD_EXT_NUNCHUK) {
|
||||||
|
inputData.trigger |= remapWiiMoteButtons(kpadStatus.trigger);
|
||||||
|
inputData.hold |= remapWiiMoteButtons(kpadStatus.hold);
|
||||||
|
inputData.release |= remapWiiMoteButtons(kpadStatus.release);
|
||||||
|
} else {
|
||||||
|
inputData.trigger |= remapClassicButtons(kpadStatus.classic.trigger);
|
||||||
|
inputData.hold |= remapClassicButtons(kpadStatus.classic.hold);
|
||||||
|
inputData.release |= remapClassicButtons(kpadStatus.classic.release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputUtils::Init() {
|
||||||
|
KPADInit();
|
||||||
|
WPADEnableURCC(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputUtils::DeInit() {
|
||||||
|
KPADShutdown();
|
||||||
|
}
|
17
source/InputUtils.h
Normal file
17
source/InputUtils.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vpad/input.h>
|
||||||
|
|
||||||
|
class InputUtils {
|
||||||
|
public:
|
||||||
|
typedef struct InputData {
|
||||||
|
uint32_t trigger = 0;
|
||||||
|
uint32_t hold = 0;
|
||||||
|
uint32_t release = 0;
|
||||||
|
} InputData;
|
||||||
|
|
||||||
|
static void Init();
|
||||||
|
static void DeInit();
|
||||||
|
|
||||||
|
static InputData getControllerInput();
|
||||||
|
};
|
|
@ -1,25 +1,26 @@
|
||||||
#include "MenuUtils.h"
|
#include "MenuUtils.h"
|
||||||
#include "ACTAccountInfo.h"
|
#include "ACTAccountInfo.h"
|
||||||
#include "DrawUtils.h"
|
#include "DrawUtils.h"
|
||||||
#include "icon_png.h"
|
#include "InputUtils.h"
|
||||||
|
#include "PairUtils.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "main.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include <coreinit/debug.h>
|
#include <coreinit/debug.h>
|
||||||
#include <coreinit/mcp.h>
|
#include <coreinit/filesystem_fsa.h>
|
||||||
#include <coreinit/screen.h>
|
#include <coreinit/screen.h>
|
||||||
#include <coreinit/thread.h>
|
#include <coreinit/thread.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <gx2/state.h>
|
#include <gx2/state.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <nn/act/client_cpp.h>
|
#include <mocha/mocha.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sysapp/title.h>
|
#include <sysapp/title.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <vpad/input.h>
|
|
||||||
|
|
||||||
#define AUTOBOOT_MODULE_VERSION "v0.1.3"
|
#define AUTOBOOT_MODULE_VERSION "v0.2.1"
|
||||||
|
|
||||||
const char *autoboot_config_strings[] = {
|
const char *autoboot_config_strings[] = {
|
||||||
"wiiu_menu",
|
"wiiu_menu",
|
||||||
|
@ -28,15 +29,6 @@ const char *autoboot_config_strings[] = {
|
||||||
"vwii_homebrew_channel",
|
"vwii_homebrew_channel",
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
std::string string_format(const std::string &format, Args... args) {
|
|
||||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
|
||||||
auto size = static_cast<size_t>(size_s);
|
|
||||||
auto buf = std::make_unique<char[]>(size);
|
|
||||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
|
||||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t readAutobootOption(std::string &configPath) {
|
int32_t readAutobootOption(std::string &configPath) {
|
||||||
FILE *f = fopen(configPath.c_str(), "r");
|
FILE *f = fopen(configPath.c_str(), "r");
|
||||||
if (f) {
|
if (f) {
|
||||||
|
@ -66,6 +58,55 @@ void writeAutobootOption(std::string &configPath, int32_t autobootOption) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawMenuScreen(const std::map<uint32_t, std::string> &menu, uint32_t selectedIndex, uint32_t autobootIndex, bool updatesBlocked) {
|
||||||
|
DrawUtils::beginDraw();
|
||||||
|
DrawUtils::clear(COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
// draw buttons
|
||||||
|
uint32_t index = 8 + 24 + 8 + 4;
|
||||||
|
for (uint32_t i = 0; i < menu.size(); i++) {
|
||||||
|
if (i == (uint32_t) selectedIndex) {
|
||||||
|
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) autobootIndex) ? COLOR_AUTOBOOT : COLOR_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string curName = std::next(menu.begin(), i)->second;
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(24);
|
||||||
|
DrawUtils::setFontColor((i == (uint32_t) autobootIndex) ? COLOR_AUTOBOOT : COLOR_TEXT);
|
||||||
|
DrawUtils::print(16 * 2, index + 8 + 24, curName.c_str());
|
||||||
|
index += 42 + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::setFontColor(COLOR_TEXT);
|
||||||
|
|
||||||
|
// draw top bar
|
||||||
|
DrawUtils::setFontSize(24);
|
||||||
|
DrawUtils::print(16, 6 + 24, "Boot Selector");
|
||||||
|
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||||
|
DrawUtils::setFontSize(16);
|
||||||
|
DrawUtils::print(SCREEN_WIDTH - 16, 6 + 24, AUTOBOOT_MODULE_VERSION AUTOBOOT_MODULE_VERSION_EXTRA, true);
|
||||||
|
|
||||||
|
// 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/\ue046 Clear Autoboot / \ue003/\ue045 Select Autoboot";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(autobootHints) / 2, SCREEN_HEIGHT - 8, autobootHints, true);
|
||||||
|
|
||||||
|
if (updatesBlocked) {
|
||||||
|
DrawUtils::setFontSize(10);
|
||||||
|
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 24 - 8 - 4 - 10, "Updates blocked! Hold \ue045 + \ue046 to restore Update folder", true);
|
||||||
|
} else {
|
||||||
|
DrawUtils::setFontSize(10);
|
||||||
|
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 24 - 8 - 4 - 10, "Updates not blocked! Hold \ue045 + \ue046 to delete Update folder", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::endDraw();
|
||||||
|
}
|
||||||
|
|
||||||
int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, const std::map<uint32_t, std::string> &menu) {
|
int32_t handleMenuScreen(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) {
|
||||||
|
@ -103,80 +144,53 @@ int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool redraw = true;
|
{
|
||||||
while (true) {
|
PairMenu pairMenu;
|
||||||
VPADStatus vpad{};
|
|
||||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
|
||||||
|
|
||||||
if (vpad.trigger & VPAD_BUTTON_UP) {
|
int32_t holdUpdateBlockedForFrames = 0;
|
||||||
selectedIndex--;
|
while (true) {
|
||||||
|
if (pairMenu.ProcessPairScreen()) {
|
||||||
if (selectedIndex < 0) {
|
continue;
|
||||||
selectedIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redraw = true;
|
|
||||||
} else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
|
||||||
if (!menu.empty()) {
|
|
||||||
selectedIndex++;
|
|
||||||
|
|
||||||
if ((uint32_t) selectedIndex >= menu.size()) {
|
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||||
selectedIndex = menu.size() - 1;
|
|
||||||
}
|
|
||||||
redraw = true;
|
|
||||||
}
|
|
||||||
} else if (vpad.trigger & VPAD_BUTTON_A) {
|
|
||||||
break;
|
|
||||||
} else if (vpad.trigger & VPAD_BUTTON_X) {
|
|
||||||
autobootIndex = -1;
|
|
||||||
redraw = true;
|
|
||||||
} else if (vpad.trigger & VPAD_BUTTON_Y) {
|
|
||||||
autobootIndex = selectedIndex;
|
|
||||||
redraw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redraw) {
|
if (input.trigger & VPAD_BUTTON_UP) {
|
||||||
DrawUtils::beginDraw();
|
selectedIndex--;
|
||||||
DrawUtils::clear(COLOR_BACKGROUND);
|
|
||||||
|
|
||||||
// draw buttons
|
if (selectedIndex < 0) {
|
||||||
uint32_t index = 8 + 24 + 8 + 4;
|
selectedIndex = 0;
|
||||||
for (uint32_t i = 0; i < menu.size(); i++) {
|
|
||||||
if (i == (uint32_t) selectedIndex) {
|
|
||||||
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) autobootIndex) ? COLOR_AUTOBOOT : COLOR_BORDER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string curName = std::next(menu.begin(), i)->second;
|
} else if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||||
|
if (!menu.empty()) {
|
||||||
|
selectedIndex++;
|
||||||
|
|
||||||
DrawUtils::setFontSize(24);
|
if ((uint32_t) selectedIndex >= menu.size()) {
|
||||||
DrawUtils::setFontColor((i == (uint32_t) autobootIndex) ? COLOR_AUTOBOOT : COLOR_TEXT);
|
selectedIndex = menu.size() - 1;
|
||||||
DrawUtils::print(16 * 2, index + 8 + 24, curName.c_str());
|
}
|
||||||
index += 42 + 8;
|
}
|
||||||
|
} else if (input.trigger & VPAD_BUTTON_A) {
|
||||||
|
break;
|
||||||
|
} else if (input.trigger & (VPAD_BUTTON_X | VPAD_BUTTON_MINUS)) {
|
||||||
|
autobootIndex = -1;
|
||||||
|
} else if (input.trigger & (VPAD_BUTTON_Y | VPAD_BUTTON_PLUS)) {
|
||||||
|
autobootIndex = selectedIndex;
|
||||||
|
} else if ((input.hold & (VPAD_BUTTON_PLUS | VPAD_BUTTON_MINUS)) == (VPAD_BUTTON_PLUS | VPAD_BUTTON_MINUS)) {
|
||||||
|
if (holdUpdateBlockedForFrames++ > 50) {
|
||||||
|
if (gUpdatesBlocked) {
|
||||||
|
gUpdatesBlocked = !RestoreMLCUpdateDirectory();
|
||||||
|
} else {
|
||||||
|
gUpdatesBlocked = DeleteMLCUpdateDirectory();
|
||||||
|
}
|
||||||
|
holdUpdateBlockedForFrames = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holdUpdateBlockedForFrames = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawUtils::setFontColor(COLOR_TEXT);
|
drawMenuScreen(menu, selectedIndex, autobootIndex, gUpdatesBlocked);
|
||||||
|
|
||||||
// draw top bar
|
|
||||||
DrawUtils::setFontSize(24);
|
|
||||||
DrawUtils::drawPNG(16, 2, icon_png);
|
|
||||||
DrawUtils::print(64 + 2, 6 + 24, "Boot Selector");
|
|
||||||
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
|
||||||
DrawUtils::setFontSize(16);
|
|
||||||
DrawUtils::print(SCREEN_WIDTH - 16, 6 + 24, AUTOBOOT_MODULE_VERSION AUTOBOOT_MODULE_VERSION_EXTRA, true);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +222,6 @@ int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, c
|
||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<AccountInfo>> &data) {
|
nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<AccountInfo>> &data) {
|
||||||
auto screenBuffer = DrawUtils::InitOSScreen();
|
auto screenBuffer = DrawUtils::InitOSScreen();
|
||||||
if (!screenBuffer) {
|
if (!screenBuffer) {
|
||||||
|
@ -224,26 +237,27 @@ nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<Acco
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t selected = 0;
|
int32_t selected = 0;
|
||||||
bool redraw = true;
|
{
|
||||||
while (true) {
|
PairMenu pairMenu;
|
||||||
VPADStatus vpad{};
|
while (true) {
|
||||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
if (pairMenu.ProcessPairScreen()) {
|
||||||
|
continue;
|
||||||
if (vpad.trigger & VPAD_BUTTON_UP) {
|
|
||||||
if (selected > 0) {
|
|
||||||
selected--;
|
|
||||||
redraw = true;
|
|
||||||
}
|
}
|
||||||
} else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
|
||||||
if (selected < (int32_t) data.size() - 1) {
|
|
||||||
selected++;
|
|
||||||
redraw = true;
|
|
||||||
}
|
|
||||||
} else if (vpad.trigger & VPAD_BUTTON_A) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redraw) {
|
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||||
|
if (input.trigger & VPAD_BUTTON_UP) {
|
||||||
|
if (selected > 0) {
|
||||||
|
selected--;
|
||||||
|
}
|
||||||
|
} else if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||||
|
if (selected < (int32_t) data.size() - 1) {
|
||||||
|
selected++;
|
||||||
|
}
|
||||||
|
} else if (input.trigger & VPAD_BUTTON_A) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DrawUtils::beginDraw();
|
DrawUtils::beginDraw();
|
||||||
DrawUtils::clear(COLOR_BACKGROUND);
|
DrawUtils::clear(COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
@ -314,8 +328,6 @@ nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<Acco
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawUtils::endDraw();
|
DrawUtils::endDraw();
|
||||||
|
|
||||||
redraw = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,6 +354,45 @@ nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<Acco
|
||||||
return resultSlot;
|
return resultSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawUpdateWarningScreen() {
|
||||||
|
DrawUtils::beginDraw();
|
||||||
|
DrawUtils::clear(COLOR_BACKGROUND_WARN);
|
||||||
|
|
||||||
|
DrawUtils::setFontColor(COLOR_WARNING);
|
||||||
|
|
||||||
|
// draw top bar
|
||||||
|
DrawUtils::setFontSize(48);
|
||||||
|
const char *title = "! Warning !";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 48 + 8, title, true);
|
||||||
|
DrawUtils::drawRectFilled(8, 48 + 8 + 16, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(24);
|
||||||
|
|
||||||
|
const char *message = "The update folder currently exists and is not a file.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 - 48, message, true);
|
||||||
|
message = "Your system might not be blocking updates properly!";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 - 24, message, true);
|
||||||
|
|
||||||
|
message = "Press \ue002 to block the updates! This can be reverted in the Boot Selector.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 + 24, message, true);
|
||||||
|
|
||||||
|
message = "See https://wiiu.hacks.guide/#/block-updates for more information.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 + 64 + 24, message, true);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(16);
|
||||||
|
|
||||||
|
message = "Press the SYNC Button on the Wii U console to connect a controller or GamePad.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT - 48, message, true);
|
||||||
|
|
||||||
|
// draw bottom bar
|
||||||
|
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||||
|
DrawUtils::setFontSize(18);
|
||||||
|
const char *exitHints = "\ue000 Continue without blocking / \ue001 Don't show this again";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
||||||
|
|
||||||
|
DrawUtils::endDraw();
|
||||||
|
}
|
||||||
|
|
||||||
void handleUpdateWarningScreen() {
|
void handleUpdateWarningScreen() {
|
||||||
FILE *f = fopen(UPDATE_SKIP_PATH, "r");
|
FILE *f = fopen(UPDATE_SKIP_PATH, "r");
|
||||||
if (f) {
|
if (f) {
|
||||||
|
@ -363,54 +414,38 @@ void handleUpdateWarningScreen() {
|
||||||
OSFatal("Failed to init font");
|
OSFatal("Failed to init font");
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawUtils::beginDraw();
|
{
|
||||||
DrawUtils::clear(COLOR_BACKGROUND_WARN);
|
PairMenu pairMenu;
|
||||||
|
|
||||||
DrawUtils::setFontColor(COLOR_TEXT);
|
while (true) {
|
||||||
|
if (pairMenu.ProcessPairScreen()) {
|
||||||
// draw top bar
|
continue;
|
||||||
DrawUtils::setFontSize(48);
|
}
|
||||||
const char *title = "! Warning !";
|
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 48 + 8, title, true);
|
drawUpdateWarningScreen();
|
||||||
DrawUtils::drawRectFilled(8, 48 + 8 + 16, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
|
||||||
|
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||||
DrawUtils::setFontSize(24);
|
if (input.trigger & VPAD_BUTTON_A) {
|
||||||
|
break;
|
||||||
const char *message = "The update folder currently exists and is not a file.";
|
} else if (input.trigger & VPAD_BUTTON_X) {
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 - 24, message, true);
|
gUpdatesBlocked = DeleteMLCUpdateDirectory();
|
||||||
message = "Your system might not be blocking updates properly!";
|
break;
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 + 0, message, true);
|
} else if (input.trigger & VPAD_BUTTON_B) {
|
||||||
message = "See https://wiiu.hacks.guide/#/block-updates for more information.";
|
f = fopen(UPDATE_SKIP_PATH, "w");
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 + 24, message, true);
|
if (f) {
|
||||||
|
// It's **really** important to have this text on the stack.
|
||||||
// draw bottom bar
|
// If it's read from the .rodata section the fwrite will softlock the console because the OSEffectiveToPhysical returns NULL for
|
||||||
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
// everything between 0x00800000 - 0x01000000 at this stage.
|
||||||
DrawUtils::setFontSize(18);
|
const char text[] = "If this file exists, the Autoboot Module will not warn you about not blocking updates";
|
||||||
const char *exitHints = "\ue000 Continue / \ue001 Don't show this again";
|
fputs(text, f);
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
fclose(f);
|
||||||
|
}
|
||||||
DrawUtils::endDraw();
|
break;
|
||||||
|
|
||||||
while (true) {
|
|
||||||
VPADStatus vpad{};
|
|
||||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
|
||||||
|
|
||||||
if (vpad.trigger & VPAD_BUTTON_A) {
|
|
||||||
break;
|
|
||||||
} else if (vpad.trigger & VPAD_BUTTON_B) {
|
|
||||||
f = fopen(UPDATE_SKIP_PATH, "w");
|
|
||||||
if (f) {
|
|
||||||
// It's **really** important to have this text on the stack.
|
|
||||||
// If it's read from the .rodata section the fwrite will softlock the console because the OSEffectiveToPhysical returns NULL for
|
|
||||||
// everything between 0x00800000 - 0x01000000 at this stage.
|
|
||||||
const char text[] = "If this file exists, the Autoboot Module will not warn you about not blocking updates";
|
|
||||||
fputs(text, f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawUtils::beginDraw();
|
||||||
DrawUtils::clear(COLOR_BLACK);
|
DrawUtils::clear(COLOR_BLACK);
|
||||||
DrawUtils::endDraw();
|
DrawUtils::endDraw();
|
||||||
|
|
||||||
|
@ -419,6 +454,32 @@ void handleUpdateWarningScreen() {
|
||||||
free(screenBuffer);
|
free(screenBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawDiscInsert(bool wrongDiscInserted) {
|
||||||
|
DrawUtils::beginDraw();
|
||||||
|
DrawUtils::clear(COLOR_BACKGROUND);
|
||||||
|
DrawUtils::setFontColor(COLOR_TEXT);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(48);
|
||||||
|
|
||||||
|
if (wrongDiscInserted) {
|
||||||
|
const char *title = "The disc inserted into the console";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 48 + 8, title, true);
|
||||||
|
title = "is for a different software title.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 2 * 48 + 8, title, true);
|
||||||
|
title = "Please change the disc.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 4 * 48 + 8, title, true);
|
||||||
|
} else {
|
||||||
|
const char *title = "Please insert a disc.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 48 + 8, title, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(18);
|
||||||
|
const char *exitHints = "\ue000 Launch Wii U Menu";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
||||||
|
|
||||||
|
DrawUtils::endDraw();
|
||||||
|
}
|
||||||
|
|
||||||
bool handleDiscInsertScreen(uint64_t expectedTitleId, uint64_t *titleIdToLaunch) {
|
bool handleDiscInsertScreen(uint64_t expectedTitleId, uint64_t *titleIdToLaunch) {
|
||||||
if (titleIdToLaunch == nullptr) {
|
if (titleIdToLaunch == nullptr) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("titleIdToLaunch is NULL");
|
DEBUG_FUNCTION_LINE_ERR("titleIdToLaunch is NULL");
|
||||||
|
@ -467,56 +528,43 @@ bool handleDiscInsertScreen(uint64_t expectedTitleId, uint64_t *titleIdToLaunch)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawUtils::beginDraw();
|
|
||||||
DrawUtils::clear(COLOR_BACKGROUND);
|
|
||||||
DrawUtils::setFontColor(COLOR_TEXT);
|
|
||||||
|
|
||||||
DrawUtils::setFontSize(48);
|
|
||||||
|
|
||||||
if (wrongDiscInserted) {
|
|
||||||
const char *title = "The disc inserted into the console";
|
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 48 + 8, title, true);
|
|
||||||
title = "is for a different software title.";
|
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 2 * 48 + 8, title, true);
|
|
||||||
title = "Please change the disc.";
|
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 4 * 48 + 8, title, true);
|
|
||||||
} else {
|
|
||||||
const char *title = "Please insert a disc.";
|
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 48 + 8, title, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawUtils::setFontSize(18);
|
|
||||||
const char *exitHints = "\ue000 Launch Wii U Menu";
|
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
|
||||||
|
|
||||||
DrawUtils::endDraw();
|
|
||||||
|
|
||||||
// When an unexpected disc was inserted we need to eject it first.
|
// When an unexpected disc was inserted we need to eject it first.
|
||||||
bool allowDisc = !wrongDiscInserted;
|
bool allowDisc = !wrongDiscInserted;
|
||||||
while (true) {
|
|
||||||
VPADStatus vpad{};
|
|
||||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
|
||||||
|
|
||||||
if (vpad.trigger & VPAD_BUTTON_A) {
|
{
|
||||||
result = false;
|
PairMenu pairMenu;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetTitleIdOfDisc(&titleIdOfDisc, &discInserted)) {
|
while (true) {
|
||||||
if (discInserted) {
|
if (pairMenu.ProcessPairScreen()) {
|
||||||
if (!allowDisc) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
*titleIdToLaunch = titleIdOfDisc;
|
drawDiscInsert(wrongDiscInserted);
|
||||||
DEBUG_FUNCTION_LINE("Disc inserted! %016llX", titleIdOfDisc);
|
|
||||||
result = true;
|
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||||
|
if (input.trigger & VPAD_BUTTON_A) {
|
||||||
|
result = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
allowDisc = true;
|
|
||||||
|
if (GetTitleIdOfDisc(&titleIdOfDisc, &discInserted)) {
|
||||||
|
if (discInserted) {
|
||||||
|
if (!allowDisc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*titleIdToLaunch = titleIdOfDisc;
|
||||||
|
DEBUG_FUNCTION_LINE("Disc inserted! %016llX", titleIdOfDisc);
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allowDisc = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawUtils::beginDraw();
|
||||||
DrawUtils::clear(COLOR_BLACK);
|
DrawUtils::clear(COLOR_BLACK);
|
||||||
DrawUtils::endDraw();
|
DrawUtils::endDraw();
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
#define COLOR_WHITE Color(0xffffffff)
|
#define COLOR_WHITE Color(0xffffffff)
|
||||||
#define COLOR_BLACK Color(0, 0, 0, 255)
|
#define COLOR_BLACK Color(0, 0, 0, 255)
|
||||||
#define COLOR_BACKGROUND COLOR_BLACK
|
#define COLOR_BACKGROUND COLOR_BLACK
|
||||||
#define COLOR_BACKGROUND_WARN Color(255, 40, 0, 255)
|
#define COLOR_BACKGROUND_WARN Color(255, 251, 4, 255)
|
||||||
#define COLOR_TEXT COLOR_WHITE
|
#define COLOR_TEXT COLOR_WHITE
|
||||||
|
#define COLOR_WARNING COLOR_BLACK
|
||||||
#define COLOR_TEXT2 Color(0xB3ffffff)
|
#define COLOR_TEXT2 Color(0xB3ffffff)
|
||||||
#define COLOR_AUTOBOOT Color(0xaeea00ff)
|
#define COLOR_AUTOBOOT Color(0xaeea00ff)
|
||||||
#define COLOR_BORDER Color(204, 204, 204, 255)
|
#define COLOR_BORDER Color(204, 204, 204, 255)
|
||||||
|
|
241
source/PairUtils.cpp
Normal file
241
source/PairUtils.cpp
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
#include "PairUtils.h"
|
||||||
|
#include "DrawUtils.h"
|
||||||
|
#include "InputUtils.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <coreinit/thread.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <nn/ccr/sys.h>
|
||||||
|
#include <padscore/kpad.h>
|
||||||
|
#include <padscore/wpad.h>
|
||||||
|
#include <vpad/input.h>
|
||||||
|
|
||||||
|
void PairMenu::drawPairKPADScreen() const {
|
||||||
|
DrawUtils::beginDraw();
|
||||||
|
DrawUtils::clear(COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
DrawUtils::setFontColor(COLOR_TEXT);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(26);
|
||||||
|
|
||||||
|
std::string textLine1 = "Press the SYNC Button on the controller you want to pair.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine1.c_str()) / 2, 40, textLine1.c_str(), true);
|
||||||
|
|
||||||
|
|
||||||
|
WPADExtensionType ext{};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
bool isConnected = WPADProbe((WPADChan) i, &ext) == 0;
|
||||||
|
std::string textLine = string_format("Slot %d: ", i + 1);
|
||||||
|
if (isConnected) {
|
||||||
|
textLine += ext == WPAD_EXT_PRO_CONTROLLER ? "Pro Controller" : "Wiimote";
|
||||||
|
} else {
|
||||||
|
textLine += "No controller";
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::print(300, 140 + (i * 30), textLine.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(26);
|
||||||
|
|
||||||
|
std::string gamepadSyncText1 = "If you are pairing a Wii U GamePad, press the SYNC Button";
|
||||||
|
std::string gamepadSyncText2 = "on your Wii U console one more time";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(gamepadSyncText1.c_str()) / 2, SCREEN_HEIGHT - 100, gamepadSyncText1.c_str(), true);
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(gamepadSyncText2.c_str()) / 2, SCREEN_HEIGHT - 70, gamepadSyncText2.c_str(), true);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(16);
|
||||||
|
|
||||||
|
const char *exitHints = "Press \ue001 to return";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
||||||
|
|
||||||
|
DrawUtils::endDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PairMenu::drawPairScreen() const {
|
||||||
|
DrawUtils::beginDraw();
|
||||||
|
DrawUtils::clear(COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
DrawUtils::setFontColor(COLOR_TEXT);
|
||||||
|
|
||||||
|
// Convert the pin to symbols and set the text
|
||||||
|
static char pinSymbols[][4] = {
|
||||||
|
"\u2660",
|
||||||
|
"\u2665",
|
||||||
|
"\u2666",
|
||||||
|
"\u2663"};
|
||||||
|
|
||||||
|
uint32_t pincode = mGamePadPincode;
|
||||||
|
|
||||||
|
std::string pin = std::string(pinSymbols[(pincode / 1000) % 10]) +
|
||||||
|
pinSymbols[(pincode / 100) % 10] +
|
||||||
|
pinSymbols[(pincode / 10) % 10] +
|
||||||
|
pinSymbols[pincode % 10];
|
||||||
|
|
||||||
|
std::string textLine1 = "Press the SYNC Button on the Wii U GamePad,";
|
||||||
|
std::string textLine2 = "and enter the four symbols shown below.";
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(26);
|
||||||
|
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine1.c_str()) / 2, 60, textLine1.c_str(), true);
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine2.c_str()) / 2, 100, textLine2.c_str(), true);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(100);
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(pin.c_str()) / 2, (SCREEN_HEIGHT / 2) + 40, pin.c_str(), true);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(20);
|
||||||
|
|
||||||
|
std::string textLine3 = string_format("(%d seconds remaining) ", mGamePadSyncTimeout - (uint32_t) (OSTicksToSeconds(OSGetTime() - mSyncGamePadStartTime)));
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine3.c_str()) / 2, SCREEN_HEIGHT - 80, textLine3.c_str(), true);
|
||||||
|
|
||||||
|
DrawUtils::setFontSize(26);
|
||||||
|
|
||||||
|
std::string textLine4 = "Press the SYNC Button on the Wii U console to exit.";
|
||||||
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine4.c_str()) / 2, SCREEN_HEIGHT - 40, textLine4.c_str(), true);
|
||||||
|
|
||||||
|
DrawUtils::endDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
PairMenu::PairMenu() {
|
||||||
|
CCRSysInit();
|
||||||
|
|
||||||
|
mState = STATE_WAIT;
|
||||||
|
mGamePadSyncTimeout = 120;
|
||||||
|
|
||||||
|
// Initialize IM
|
||||||
|
mIMHandle = IM_Open();
|
||||||
|
if (mIMHandle < 0) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("PairMenu: IM_Open failed");
|
||||||
|
OSFatal("PairMenu: IM_Open failed");
|
||||||
|
}
|
||||||
|
mIMRequest = (IMRequest *) memalign(0x40, sizeof(IMRequest));
|
||||||
|
|
||||||
|
// Allocate a separate request for IM_CancelGetEventNotify to avoid conflict with the pending IM_GetEventNotify request
|
||||||
|
mIMCancelRequest = (IMRequest *) memalign(0x40, sizeof(IMRequest));
|
||||||
|
|
||||||
|
if (!mIMRequest || !mIMCancelRequest) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to allocate im request");
|
||||||
|
OSFatal("PairMenu: Failed to allocate im request");
|
||||||
|
}
|
||||||
|
|
||||||
|
mIMEventMask = IM_EVENT_SYNC;
|
||||||
|
|
||||||
|
// Notify about sync button events
|
||||||
|
IM_GetEventNotify(mIMHandle, mIMRequest, &mIMEventMask, PairMenu::SyncButtonCallback, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PairMenu::~PairMenu() {
|
||||||
|
// Close IM
|
||||||
|
IM_CancelGetEventNotify(mIMHandle, mIMCancelRequest, nullptr, nullptr);
|
||||||
|
IM_Close(mIMHandle);
|
||||||
|
if (mIMCancelRequest) {
|
||||||
|
free(mIMCancelRequest);
|
||||||
|
mIMCancelRequest = {};
|
||||||
|
}
|
||||||
|
if (mIMRequest) {
|
||||||
|
free(mIMRequest);
|
||||||
|
mIMRequest = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deinit CCRSys
|
||||||
|
CCRSysExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool WPADStartSyncDevice();
|
||||||
|
|
||||||
|
bool PairMenu::ProcessPairScreen() {
|
||||||
|
switch (mState) {
|
||||||
|
case STATE_SYNC_WPAD: {
|
||||||
|
// WPAD syncing stops after ~18 seconds, make sure to restart it.
|
||||||
|
if ((uint32_t) OSTicksToSeconds(OSGetTime() - mSyncWPADStartTime) >= 18) {
|
||||||
|
WPADStartSyncDevice();
|
||||||
|
mSyncWPADStartTime = OSGetTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||||
|
|
||||||
|
// Stop syncing when pressing A or B.
|
||||||
|
if (input.trigger & (VPAD_BUTTON_A | VPAD_BUTTON_B)) {
|
||||||
|
mState = STATE_WAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STATE_SYNC_GAMEPAD: {
|
||||||
|
if (CCRSysGetPincode(&mGamePadPincode) != 0) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("CCRSysGetPincode failed");
|
||||||
|
mState = STATE_WAIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start pairing to slot 1 (second gamepad)
|
||||||
|
if (CCRSysStartPairing(0, mGamePadSyncTimeout) != 0) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("CCRSysStartPairing failed.");
|
||||||
|
mState = STATE_WAIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pairing has started, save start time
|
||||||
|
mSyncGamePadStartTime = OSGetTime();
|
||||||
|
mState = STATE_PAIRING;
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE("Started GamePad syncing.");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STATE_PAIRING: {
|
||||||
|
// Get the current pairing state
|
||||||
|
CCRSysPairingState pairingState = CCRSysGetPairingState();
|
||||||
|
if (pairingState == CCR_SYS_PAIRING_TIMED_OUT) {
|
||||||
|
DEBUG_FUNCTION_LINE("GamePad SYNC timed out.");
|
||||||
|
// Pairing has timed out or was cancelled
|
||||||
|
CCRSysStopPairing();
|
||||||
|
mState = STATE_WAIT;
|
||||||
|
} else if (pairingState == CCR_SYS_PAIRING_FINISHED) {
|
||||||
|
DEBUG_FUNCTION_LINE("GamePad paired.");
|
||||||
|
mState = STATE_WAIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STATE_CANCEL: {
|
||||||
|
CCRSysStopPairing();
|
||||||
|
mState = STATE_WAIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STATE_WAIT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (mState) {
|
||||||
|
case STATE_WAIT: {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case STATE_SYNC_WPAD:
|
||||||
|
drawPairKPADScreen();
|
||||||
|
break;
|
||||||
|
case STATE_SYNC_GAMEPAD:
|
||||||
|
case STATE_PAIRING:
|
||||||
|
case STATE_CANCEL: {
|
||||||
|
drawPairScreen();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PairMenu::SyncButtonCallback(IOSError error, void *arg) {
|
||||||
|
auto *pairMenu = (PairMenu *) arg;
|
||||||
|
|
||||||
|
if (error == IOS_ERROR_OK && pairMenu && (pairMenu->mIMEventMask & IM_EVENT_SYNC)) {
|
||||||
|
if (pairMenu->mState == STATE_WAIT) {
|
||||||
|
pairMenu->mState = STATE_SYNC_WPAD;
|
||||||
|
// We need to restart the WPAD pairing every 18 seconds. For the timing we need to save the current time.
|
||||||
|
pairMenu->mSyncWPADStartTime = OSGetTime();
|
||||||
|
} else if (pairMenu->mState == STATE_SYNC_WPAD) {
|
||||||
|
pairMenu->mState = STATE_SYNC_GAMEPAD;
|
||||||
|
} else if (pairMenu->mState == STATE_SYNC_GAMEPAD || pairMenu->mState == STATE_PAIRING) {
|
||||||
|
pairMenu->mState = STATE_CANCEL;
|
||||||
|
}
|
||||||
|
OSMemoryBarrier();
|
||||||
|
IM_GetEventNotify(pairMenu->mIMHandle, pairMenu->mIMRequest, &pairMenu->mIMEventMask, PairMenu::SyncButtonCallback, pairMenu);
|
||||||
|
}
|
||||||
|
}
|
45
source/PairUtils.h
Normal file
45
source/PairUtils.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MenuUtils.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <coreinit/im.h>
|
||||||
|
#include <coreinit/ios.h>
|
||||||
|
#include <coreinit/time.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <nn/ccr/sys.h>
|
||||||
|
|
||||||
|
|
||||||
|
class PairMenu {
|
||||||
|
public:
|
||||||
|
PairMenu();
|
||||||
|
|
||||||
|
~PairMenu();
|
||||||
|
|
||||||
|
bool ProcessPairScreen();
|
||||||
|
|
||||||
|
static void SyncButtonCallback(IOSError error, void *arg);
|
||||||
|
|
||||||
|
void drawPairScreen() const;
|
||||||
|
|
||||||
|
void drawPairKPADScreen() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum PairMenuState {
|
||||||
|
STATE_WAIT, // Wait for SYNC button press
|
||||||
|
STATE_SYNC_WPAD,
|
||||||
|
STATE_SYNC_GAMEPAD,
|
||||||
|
STATE_PAIRING,
|
||||||
|
STATE_CANCEL,
|
||||||
|
};
|
||||||
|
|
||||||
|
IOSHandle mIMHandle{};
|
||||||
|
IMRequest *mIMRequest{};
|
||||||
|
IMRequest *mIMCancelRequest{};
|
||||||
|
OSTime mSyncWPADStartTime = 0;
|
||||||
|
OSTime mSyncGamePadStartTime = 0;
|
||||||
|
uint32_t mGamePadPincode = 0;
|
||||||
|
PairMenuState mState = STATE_WAIT;
|
||||||
|
uint32_t mGamePadSyncTimeout = 120;
|
||||||
|
IMEventMask mIMEventMask{};
|
||||||
|
};
|
|
@ -7,17 +7,25 @@
|
||||||
|
|
||||||
#include <coreinit/exit.h>
|
#include <coreinit/exit.h>
|
||||||
#include <coreinit/foreground.h>
|
#include <coreinit/foreground.h>
|
||||||
|
#include <coreinit/launch.h>
|
||||||
#include <coreinit/memdefaultheap.h>
|
#include <coreinit/memdefaultheap.h>
|
||||||
#include <coreinit/thread.h>
|
#include <coreinit/thread.h>
|
||||||
#include <nn/acp/title.h>
|
#include <nn/acp/title.h>
|
||||||
#include <nn/act/client_cpp.h>
|
#include <nn/act/client_cpp.h>
|
||||||
#include <nn/ccr/sys_caffeine.h>
|
#include <nn/ccr/sys_caffeine.h>
|
||||||
#include <nn/sl.h>
|
#include <nn/sl.h>
|
||||||
|
#include <nsysccr/cdc.h>
|
||||||
|
#include <optional>
|
||||||
#include <proc_ui/procui.h>
|
#include <proc_ui/procui.h>
|
||||||
|
#include <rpxloader/rpxloader.h>
|
||||||
#include <sysapp/launch.h>
|
#include <sysapp/launch.h>
|
||||||
#include <sysapp/title.h>
|
#include <sysapp/title.h>
|
||||||
|
|
||||||
extern "C" void __fini_wut();
|
extern "C" void __fini_wut();
|
||||||
|
extern "C" void CCRSysCaffeineBootCheckAbort();
|
||||||
|
|
||||||
|
#define UPPER_TITLE_ID_HOMEBREW 0x0005000F
|
||||||
|
#define TITLE_ID_HOMEBREW_MASK (((uint64_t) UPPER_TITLE_ID_HOMEBREW) << 32)
|
||||||
|
|
||||||
static void StartAppletAndExit() {
|
static void StartAppletAndExit() {
|
||||||
DEBUG_FUNCTION_LINE("Wait for applet");
|
DEBUG_FUNCTION_LINE("Wait for applet");
|
||||||
|
@ -79,44 +87,131 @@ void loadConsoleAccount(const char *data_uuid) {
|
||||||
nn::act::Finalize();
|
nn::act::Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getQuickBoot() {
|
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{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class QuickStartAutoAbort {
|
||||||
|
public:
|
||||||
|
QuickStartAutoAbort() {
|
||||||
|
OSCreateAlarm(&mAlarm);
|
||||||
|
OSSetPeriodicAlarm(&mDRCConnectedAlarm,
|
||||||
|
OSSecondsToTicks(10),
|
||||||
|
OSSecondsToTicks(1),
|
||||||
|
&AbortOnDRCDisconnect);
|
||||||
|
OSSetAlarm(&mAlarm, OSSecondsToTicks(120), AbortQuickStartTitle);
|
||||||
|
mDRCConnected = IsDRCConnected();
|
||||||
|
}
|
||||||
|
~QuickStartAutoAbort() {
|
||||||
|
OSCancelAlarm(&mDRCConnectedAlarm);
|
||||||
|
OSCancelAlarm(&mAlarm);
|
||||||
|
|
||||||
|
// Reconnect the DRC if it was connected at launch but then disconnected;
|
||||||
|
if (mDRCConnected && !IsDRCConnected()) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("Wake up GamePad");
|
||||||
|
CCRCDCWowlWakeDrcArg args = {.state = 1};
|
||||||
|
CCRCDCWowlWakeDrc(&args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsDRCConnected() {
|
||||||
|
CCRCDCDrcState state = {};
|
||||||
|
CCRCDCSysGetDrcState(CCR_CDC_DESTINATION_DRC0, &state);
|
||||||
|
return state.state != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AbortQuickStartTitle(OSAlarm *alarm, OSContext *) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("Selecting a title takes too long, lets abort the quick start menu");
|
||||||
|
CCRSysCaffeineBootCheckAbort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AbortOnDRCDisconnect(OSAlarm *alarm, OSContext *) {
|
||||||
|
if (!IsDRCConnected()) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("GamePad was disconnected, lets abort the quick start menu");
|
||||||
|
CCRSysCaffeineBootCheckAbort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
OSAlarm mDRCConnectedAlarm{};
|
||||||
|
OSAlarm mAlarm{};
|
||||||
|
bool mDRCConnected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool launchQuickStartTitle() {
|
||||||
|
// 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.
|
||||||
auto bootCheck = CCRSysCaffeineBootCheck();
|
auto bootCheck = CCRSysCaffeineBootCheck();
|
||||||
if (bootCheck == 0) {
|
if (bootCheck == 0) {
|
||||||
nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap);
|
nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap);
|
||||||
char path[0x80];
|
char path[0x80];
|
||||||
nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000); // ECO process
|
nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000); // ECO process
|
||||||
FSCmdBlock cmdBlock;
|
nn::sl::LaunchInfoDatabase launchInfoDatabase;
|
||||||
FSInitCmdBlock(&cmdBlock);
|
nn::sl::LaunchInfo info;
|
||||||
|
{
|
||||||
auto fileStream = new nn::sl::FileStream;
|
// In theory the region doesn't even matter.
|
||||||
auto *fsClient = (FSClient *) memalign(0x40, sizeof(FSClient));
|
// The region is to load a "system table" into the LaunchInfoDatabase which provides the LaunchInfos for
|
||||||
if (!fsClient) {
|
// the Wii U Menu and System Settings. In the code below we check for all possible System Settings title id and
|
||||||
DEBUG_FUNCTION_LINE("Couldn't alloc memory for fsClient.");
|
// have a fallback to the Wii U Menu... This means we could get away a wrong region, but let's use the correct one
|
||||||
return false;
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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
|
CCRAppLaunchParam data; // load sys caffeine data
|
||||||
// load app launch param
|
// load app launch param
|
||||||
CCRSysCaffeineGetAppLaunchParam(&data);
|
CCRSysCaffeineGetAppLaunchParam(&data);
|
||||||
|
|
||||||
|
if (data.launchInfoDatabaseEntryId == 1) { // This id is hardcoded into the nn_sl.rpl
|
||||||
|
DEBUG_FUNCTION_LINE("Launch Quick Start Settings");
|
||||||
|
SysAppSettingsArgs args{};
|
||||||
|
args.jumpTo = SYS_SETTINGS_JUMP_TO_QUICK_START_SETTINGS; // quick start settings
|
||||||
|
_SYSLaunchSettings(&args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
loadConsoleAccount(data.uuid);
|
loadConsoleAccount(data.uuid);
|
||||||
|
|
||||||
// get launch info for id
|
auto result = launchInfoDatabase.GetLaunchInfoById(&info, data.launchInfoDatabaseEntryId);
|
||||||
nn::sl::LaunchInfo info;
|
|
||||||
auto result = database->GetLaunchInfoById(&info, data.titleId);
|
|
||||||
|
|
||||||
delete database;
|
|
||||||
delete fileStream;
|
|
||||||
|
|
||||||
FSDelClient(fsClient, FS_ERROR_FLAG_NONE);
|
|
||||||
free(fsClient);
|
|
||||||
|
|
||||||
nn::sl::Finalize();
|
nn::sl::Finalize();
|
||||||
|
|
||||||
|
@ -125,6 +220,17 @@ bool getQuickBoot() {
|
||||||
return false;
|
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 ||
|
if (info.titleId == 0x0005001010040000L ||
|
||||||
info.titleId == 0x0005001010040100L ||
|
info.titleId == 0x0005001010040100L ||
|
||||||
info.titleId == 0x0005001010040200L) {
|
info.titleId == 0x0005001010040200L) {
|
||||||
|
@ -132,6 +238,14 @@ bool getQuickBoot() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info.titleId == 0x0005001010047000L ||
|
||||||
|
info.titleId == 0x0005001010047100L ||
|
||||||
|
info.titleId == 0x0005001010047200L) {
|
||||||
|
DEBUG_FUNCTION_LINE("Launch System Settings");
|
||||||
|
_SYSLaunchSettings(nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (info.titleId == 0x000500301001220AL ||
|
if (info.titleId == 0x000500301001220AL ||
|
||||||
info.titleId == 0x000500301001210AL ||
|
info.titleId == 0x000500301001210AL ||
|
||||||
info.titleId == 0x000500301001200AL) {
|
info.titleId == 0x000500301001200AL) {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
bool getQuickBoot();
|
bool launchQuickStartTitle();
|
|
@ -90,23 +90,21 @@ void initExternalStorage() {
|
||||||
// the lib before actually using it.
|
// the lib before actually using it.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int connectedStorage = 0;
|
int numConnectedStorage;
|
||||||
if ((connectedStorage = numberUSBStorageDevicesConnected()) <= 0) {
|
int maxTries = 1200; // Wait up to 20 seconds, like the Wii U Menu
|
||||||
nn::spm::Initialize();
|
if ((numConnectedStorage = numberUSBStorageDevicesConnected()) <= 0) {
|
||||||
InitEmptyExternalStorage();
|
maxTries = 1; // Only try once if no USBStorageDrive is connected
|
||||||
nn::spm::Finalize();
|
} else {
|
||||||
return;
|
DEBUG_FUNCTION_LINE("Connected StorageDevices = %d", numConnectedStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE("Connected StorageDevices = %d", connectedStorage);
|
|
||||||
|
|
||||||
nn::spm::Initialize();
|
nn::spm::Initialize();
|
||||||
|
|
||||||
nn::spm::StorageListItem items[0x20];
|
nn::spm::StorageListItem items[0x20];
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
while (tries < 1200) { // Wait up to 20 seconds, like the Wii U Menu
|
while (tries < maxTries) {
|
||||||
int32_t numItems = nn::spm::GetStorageList(items, 0x20);
|
int32_t numItems = nn::spm::GetStorageList(items, 0x20);
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE("Number of items: %d", numItems);
|
DEBUG_FUNCTION_LINE("Number of items: %d", numItems);
|
||||||
|
@ -127,7 +125,7 @@ void initExternalStorage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found || (connectedStorage == numItems)) {
|
if (found || (numConnectedStorage == numItems)) {
|
||||||
DEBUG_FUNCTION_LINE("Found all expected items, breaking.");
|
DEBUG_FUNCTION_LINE("Found all expected items, breaking.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +133,9 @@ void initExternalStorage() {
|
||||||
tries++;
|
tries++;
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
DEBUG_FUNCTION_LINE("USB Storage is connected but either it doesn't have a WFS partition or we ran into a timeout.");
|
if (numConnectedStorage > 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("USB Storage is connected but either it doesn't have a WFS partition or we ran into a timeout.");
|
||||||
|
}
|
||||||
InitEmptyExternalStorage();
|
InitEmptyExternalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ extern "C" {
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "## WARN## ", "", FMT, ##ARGS)
|
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "## WARN## ", "", FMT, ##ARGS)
|
||||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
||||||
|
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "## INFO## ", "", FMT, ##ARGS)
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
|
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ extern "C" {
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "## WARN## ", "\n", FMT, ##ARGS)
|
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "## WARN## ", "\n", FMT, ##ARGS)
|
||||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
||||||
|
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "## INFO## ", "\n", FMT, ##ARGS)
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);
|
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
#include "BootUtils.h"
|
#include "BootUtils.h"
|
||||||
#include "DrawUtils.h"
|
#include "DrawUtils.h"
|
||||||
|
#include "InputUtils.h"
|
||||||
#include "MenuUtils.h"
|
#include "MenuUtils.h"
|
||||||
#include "QuickStartUtils.h"
|
#include "QuickStartUtils.h"
|
||||||
#include "StorageUtils.h"
|
#include "StorageUtils.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include <coreinit/debug.h>
|
#include <coreinit/debug.h>
|
||||||
#include <coreinit/filesystem_fsa.h>
|
#include <coreinit/filesystem_fsa.h>
|
||||||
|
#include <coreinit/title.h>
|
||||||
#include <gx2/state.h>
|
#include <gx2/state.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <mocha/mocha.h>
|
#include <mocha/mocha.h>
|
||||||
|
#include <rpxloader/rpxloader.h>
|
||||||
#include <sndcore2/core.h>
|
#include <sndcore2/core.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sysapp/launch.h>
|
||||||
#include <vpad/input.h>
|
#include <vpad/input.h>
|
||||||
|
|
||||||
void clearScreen() {
|
void clearScreen() {
|
||||||
|
@ -27,19 +31,35 @@ void clearScreen() {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool gUpdatesBlocked = false;
|
||||||
|
|
||||||
int32_t main(int32_t argc, char **argv) {
|
int32_t main(int32_t argc, char **argv) {
|
||||||
initLogging();
|
initLogging();
|
||||||
DEBUG_FUNCTION_LINE("Hello from Autoboot Module");
|
DEBUG_FUNCTION_LINE("Hello from Autoboot Module");
|
||||||
AXInit();
|
AXInit();
|
||||||
AXQuit();
|
AXQuit();
|
||||||
|
|
||||||
|
InputUtils::Init();
|
||||||
|
|
||||||
// Clear screen to avoid screen corruptions when loading the Wii U Menu
|
// Clear screen to avoid screen corruptions when loading the Wii U Menu
|
||||||
clearScreen();
|
clearScreen();
|
||||||
|
|
||||||
initExternalStorage();
|
initExternalStorage();
|
||||||
|
|
||||||
if (getQuickBoot()) {
|
// Use librpxloader.
|
||||||
|
RPXLoaderStatus error3;
|
||||||
|
if ((error3 = RPXLoader_InitLibrary()) != RPX_LOADER_RESULT_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("AutobootModule: Failed to init RPXLoader. This can be ignored when not running Aroma. Error %s [%d]", RPXLoader_GetStatusStr(error3), error3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in System Transfer context we need to restart the app to actually
|
||||||
|
if (OSGetTitleID() == 0x0005001010062000L || OSGetTitleID() == 0x0005001010062100L || OSGetTitleID() == 0x0005001010062200L) {
|
||||||
|
_SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr);
|
||||||
|
deinitLogging();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (launchQuickStartTitle()) {
|
||||||
deinitLogging();
|
deinitLogging();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -48,19 +68,26 @@ int32_t main(int32_t argc, char **argv) {
|
||||||
OSFatal("AutobootModule: Mocha_InitLibrary failed");
|
OSFatal("AutobootModule: Mocha_InitLibrary failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
VPADStatus vpad{};
|
InputUtils::InputData buttons = InputUtils::getControllerInput();
|
||||||
// Buffer vpad read.
|
|
||||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
|
||||||
|
|
||||||
FSAInit();
|
FSAInit();
|
||||||
auto client = FSAAddClient(nullptr);
|
auto client = FSAAddClient(nullptr);
|
||||||
if (client > 0) {
|
if (client > 0) {
|
||||||
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
||||||
// test if the update folder exists
|
// test if the update folder exists
|
||||||
FSADirectoryHandle dirHandle;
|
FSADirectoryHandle dirHandle{};
|
||||||
if (FSAOpenDir(client, "/vol/storage_mlc01/sys/update", &dirHandle) >= 0) {
|
if (FSAOpenDir(client, "/vol/storage_mlc01/sys/update", &dirHandle) >= 0) {
|
||||||
FSACloseDir(client, dirHandle);
|
FSACloseDir(client, dirHandle);
|
||||||
|
gUpdatesBlocked = false;
|
||||||
handleUpdateWarningScreen();
|
handleUpdateWarningScreen();
|
||||||
|
} else {
|
||||||
|
FSAStat st{};
|
||||||
|
if (FSAGetStat(client, "/vol/storage_mlc01/sys/update", &st) != FS_ERROR_OK) {
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("Created \"/vol/storage_mlc01/sys/update\" as file");
|
||||||
|
FSAFileHandle fd;
|
||||||
|
FSAOpenFileEx(client, "/vol/storage_mlc01/sys/update", "w", static_cast<FSMode>(0x666), FS_OPEN_FLAG_NONE, 0, &fd);
|
||||||
|
}
|
||||||
|
gUpdatesBlocked = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSA Client");
|
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSA Client");
|
||||||
|
@ -99,7 +126,7 @@ int32_t main(int32_t argc, char **argv) {
|
||||||
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) ||
|
||||||
(vpad.hold & VPAD_BUTTON_PLUS)) {
|
(buttons.hold & VPAD_BUTTON_PLUS)) {
|
||||||
bootSelection = handleMenuScreen(configPath, bootSelection, menu);
|
bootSelection = handleMenuScreen(configPath, bootSelection, menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +160,7 @@ int32_t main(int32_t argc, char **argv) {
|
||||||
bootWiiUMenu();
|
bootWiiUMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputUtils::DeInit();
|
||||||
Mocha_DeInitLibrary();
|
Mocha_DeInitLibrary();
|
||||||
deinitLogging();
|
deinitLogging();
|
||||||
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
extern bool gUpdatesBlocked;
|
|
@ -1,5 +1,7 @@
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include <coreinit/filesystem_fsa.h>
|
||||||
#include <coreinit/mcp.h>
|
#include <coreinit/mcp.h>
|
||||||
|
#include <mocha/mocha.h>
|
||||||
|
|
||||||
bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent) {
|
bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent) {
|
||||||
if (discPresent) {
|
if (discPresent) {
|
||||||
|
@ -30,4 +32,48 @@ bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeleteMLCUpdateDirectory() {
|
||||||
|
bool result = false;
|
||||||
|
auto client = FSAAddClient(nullptr);
|
||||||
|
if (client > 0) {
|
||||||
|
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
||||||
|
if (FSARemove(client, "/vol/storage_mlc01/sys/update") != FS_ERROR_OK) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to remove update directory");
|
||||||
|
} else {
|
||||||
|
FSAFileHandle fd;
|
||||||
|
if (FSAOpenFileEx(client, "/vol/storage_mlc01/sys/update", "w", static_cast<FSMode>(0x666), FS_OPEN_FLAG_NONE, 0, &fd) != FS_ERROR_OK) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to create update file");
|
||||||
|
}
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSA Client");
|
||||||
|
}
|
||||||
|
FSADelClient(client);
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to create FSA Client");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreMLCUpdateDirectory() {
|
||||||
|
bool result = false;
|
||||||
|
auto client = FSAAddClient(nullptr);
|
||||||
|
if (client > 0) {
|
||||||
|
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
||||||
|
FSARemove(client, "/vol/storage_mlc01/sys/update"); // Remove any existing files
|
||||||
|
if (FSAMakeDir(client, "/vol/storage_mlc01/sys/update", static_cast<FSMode>(0x666)) != FS_ERROR_OK) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to restore update directory");
|
||||||
|
}
|
||||||
|
result = true;
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSA Client");
|
||||||
|
}
|
||||||
|
FSADelClient(client);
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to create FSA Client");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
|
@ -19,4 +19,17 @@ std::shared_ptr<T> make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::
|
||||||
return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
|
return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent);
|
template<typename... Args>
|
||||||
|
std::string string_format(const std::string &format, Args... args) {
|
||||||
|
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||||
|
auto size = static_cast<size_t>(size_s);
|
||||||
|
auto buf = std::make_unique<char[]>(size);
|
||||||
|
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||||
|
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent);
|
||||||
|
|
||||||
|
bool DeleteMLCUpdateDirectory();
|
||||||
|
|
||||||
|
bool RestoreMLCUpdateDirectory();
|
Loading…
Reference in New Issue
Block a user