2022-02-02 19:57:14 +01:00
|
|
|
#include "MenuUtils.h"
|
2022-08-25 19:54:10 +02:00
|
|
|
#include "ACTAccountInfo.h"
|
2022-02-02 19:57:14 +01:00
|
|
|
#include "DrawUtils.h"
|
2022-08-25 19:54:10 +02:00
|
|
|
#include "icon_png.h"
|
|
|
|
#include "logger.h"
|
2023-04-02 11:59:16 +02:00
|
|
|
#include "utils.h"
|
2023-01-11 10:48:58 +01:00
|
|
|
#include "version.h"
|
2022-01-16 01:04:43 +01:00
|
|
|
#include <coreinit/debug.h>
|
2023-04-02 11:59:16 +02:00
|
|
|
#include <coreinit/mcp.h>
|
2022-02-02 19:57:14 +01:00
|
|
|
#include <coreinit/screen.h>
|
2023-04-02 11:59:16 +02:00
|
|
|
#include <coreinit/thread.h>
|
2022-08-25 19:54:10 +02:00
|
|
|
#include <cstring>
|
2022-01-16 01:04:43 +01:00
|
|
|
#include <gx2/state.h>
|
2022-08-25 19:54:10 +02:00
|
|
|
#include <malloc.h>
|
2022-01-18 14:17:15 +01:00
|
|
|
#include <memory>
|
2022-02-02 19:57:14 +01:00
|
|
|
#include <nn/act/client_cpp.h>
|
2023-03-11 17:35:42 +01:00
|
|
|
#include <fstream>
|
|
|
|
#include <sstream>
|
2022-08-25 19:54:10 +02:00
|
|
|
#include <string>
|
2023-04-02 11:59:16 +02:00
|
|
|
#include <sysapp/title.h>
|
2022-08-25 19:54:10 +02:00
|
|
|
#include <vector>
|
2022-02-02 19:57:14 +01:00
|
|
|
#include <vpad/input.h>
|
2022-01-16 01:04:43 +01:00
|
|
|
|
2023-04-02 21:28:58 +02:00
|
|
|
#define AUTOBOOT_MODULE_VERSION "v0.1.3"
|
2023-01-11 10:48:58 +01:00
|
|
|
|
2023-03-11 17:35:42 +01:00
|
|
|
// Initialize with the autoboot_base_config_strings data
|
|
|
|
std::vector<std::string> autoboot_config_strings = { autoboot_base_config_strings.begin(), autoboot_base_config_strings.end() };
|
|
|
|
std::vector<BootOption> custom_boot_options;
|
2022-01-16 01:04:43 +01:00
|
|
|
|
2022-02-02 19:57:14 +01:00
|
|
|
template<typename... Args>
|
|
|
|
std::string string_format(const std::string &format, Args... args) {
|
2022-02-03 13:03:38 +01:00
|
|
|
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
2022-02-03 14:01:11 +01:00
|
|
|
auto size = static_cast<size_t>(size_s);
|
|
|
|
auto buf = std::make_unique<char[]>(size);
|
2022-02-02 19:57:14 +01:00
|
|
|
std::snprintf(buf.get(), size, format.c_str(), args...);
|
2022-02-03 13:03:38 +01:00
|
|
|
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
2022-01-18 14:17:15 +01:00
|
|
|
}
|
2022-01-16 01:04:43 +01:00
|
|
|
|
2023-03-11 17:35:42 +01:00
|
|
|
std::string trim(const std::string& str) {
|
|
|
|
std::string result;
|
|
|
|
for (auto& ch : str) {
|
|
|
|
if (!std::isspace(ch)) {
|
|
|
|
result += ch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void readBootOptionsFromSD(const std::string &configPath) {
|
|
|
|
std::ifstream fileStream(configPath.c_str(), std::ios::in);
|
|
|
|
if (fileStream.is_open()) {
|
|
|
|
DEBUG_FUNCTION_LINE("bootOptions.cfg open");
|
|
|
|
|
|
|
|
constexpr size_t bufSize{ 256 };
|
|
|
|
for (char line[bufSize]; fileStream.getline(line, bufSize);) {
|
|
|
|
DEBUG_FUNCTION_LINE(" Line \"%s\"", line);
|
|
|
|
std::istringstream parsingLine{std::string{line}};
|
|
|
|
memset(line, 0, bufSize);
|
|
|
|
|
|
|
|
std::vector<std::string> tokens;
|
|
|
|
for (char tmp[bufSize]; parsingLine.getline(tmp, bufSize, ',');) {
|
|
|
|
DEBUG_FUNCTION_LINE(" Token \"%s\"", tmp);
|
|
|
|
tokens.push_back(trim(tmp));
|
|
|
|
memset(tmp, 0, bufSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tokens.size() == 3)
|
|
|
|
{
|
|
|
|
const std::string subSystem{ tokens[2] };
|
|
|
|
const std::string vWiiStr{ "vwii" };
|
|
|
|
const bool vWii{ std::equal(subSystem.begin(), subSystem.end(),
|
|
|
|
vWiiStr.begin(), vWiiStr.end(), [](unsigned char a, unsigned char b) {
|
|
|
|
return std::tolower(a) == std::tolower(b);
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
custom_boot_options.push_back(BootOption{tokens[0], tokens[1], vWii});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DEBUG_FUNCTION_LINE("Failed to open bootOptions.cfg");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t readAutobootOption(const std::string &configPath) {
|
|
|
|
std::ifstream fileStream(configPath.c_str(), std::ios::in);
|
|
|
|
if (fileStream.is_open()) {
|
|
|
|
std::string readOption;
|
|
|
|
fileStream >> readOption;
|
2022-01-16 01:04:43 +01:00
|
|
|
|
2023-03-11 17:35:42 +01:00
|
|
|
for (size_t i = 0; i < autoboot_config_strings.size(); ++i) {
|
|
|
|
if (autoboot_config_strings[i] == readOption) {
|
2022-01-16 01:04:43 +01:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
2023-03-11 17:35:42 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < custom_boot_options.size(); ++i) {
|
|
|
|
if (custom_boot_options[i].title == readOption) {
|
|
|
|
return BOOT_OPTION_MAX_OPTIONS + i;
|
|
|
|
}
|
|
|
|
}
|
2022-01-16 01:04:43 +01:00
|
|
|
}
|
2023-03-11 17:35:42 +01:00
|
|
|
|
2022-01-16 01:04:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-03-11 17:35:42 +01:00
|
|
|
void writeAutobootOption(const std::string &configPath, int32_t autobootOption) {
|
|
|
|
const int32_t customAutoBootOption{ autobootOption - BOOT_OPTION_MAX_OPTIONS };
|
|
|
|
std::ofstream outStream(configPath.c_str(), std::ios::out);
|
|
|
|
if (outStream.is_open()) {
|
|
|
|
if (customAutoBootOption >= 0) {
|
|
|
|
outStream << custom_boot_options[customAutoBootOption].title;
|
|
|
|
} else if (autobootOption >= 0) {
|
|
|
|
outStream << autoboot_config_strings[autobootOption];
|
2022-01-16 01:04:43 +01:00
|
|
|
} else {
|
2023-03-11 17:35:42 +01:00
|
|
|
outStream << "none";
|
2022-01-16 01:04:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-11 17:35:42 +01:00
|
|
|
int32_t handleMenuScreen(const std::string &configPath, int32_t autobootOptionInput, const std::map<uint32_t, std::string> &menu) {
|
2022-01-16 01:04:43 +01:00
|
|
|
auto screenBuffer = DrawUtils::InitOSScreen();
|
|
|
|
if (!screenBuffer) {
|
|
|
|
OSFatal("Failed to alloc memory for screen");
|
|
|
|
}
|
|
|
|
|
2022-02-03 14:01:11 +01:00
|
|
|
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
2022-01-16 01:04:43 +01:00
|
|
|
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
|
|
|
|
2022-01-18 16:29:54 +01:00
|
|
|
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
2023-01-05 17:43:20 +01:00
|
|
|
if (!DrawUtils::initFont()) {
|
|
|
|
OSFatal("Failed to init font");
|
|
|
|
}
|
2022-01-16 01:04:43 +01:00
|
|
|
|
2022-08-25 19:54:10 +02:00
|
|
|
int32_t selectedIndex = autobootOptionInput > 0 ? autobootOptionInput : 0;
|
|
|
|
int autobootIndex = autobootOptionInput;
|
|
|
|
|
|
|
|
// "Convert" id to index
|
|
|
|
int32_t offset = 0;
|
|
|
|
for (auto &item : menu) {
|
|
|
|
if ((uint32_t) selectedIndex == item.first) {
|
|
|
|
selectedIndex = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
if (autobootIndex > 0) {
|
|
|
|
offset = 0;
|
|
|
|
for (auto &item : menu) {
|
|
|
|
if ((uint32_t) autobootIndex == item.first) {
|
|
|
|
autobootIndex = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool redraw = true;
|
2022-01-16 01:04:43 +01:00
|
|
|
while (true) {
|
|
|
|
VPADStatus vpad{};
|
|
|
|
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
|
|
|
|
|
|
|
if (vpad.trigger & VPAD_BUTTON_UP) {
|
2022-08-25 19:54:10 +02:00
|
|
|
selectedIndex--;
|
2022-08-25 18:13:18 +02:00
|
|
|
|
2022-08-25 19:54:10 +02:00
|
|
|
if (selectedIndex < 0) {
|
|
|
|
selectedIndex = 0;
|
2022-01-16 01:04:43 +01:00
|
|
|
}
|
2022-08-25 19:54:10 +02:00
|
|
|
|
|
|
|
redraw = true;
|
2022-01-16 01:04:43 +01:00
|
|
|
} else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
2022-08-25 19:54:10 +02:00
|
|
|
if (!menu.empty()) {
|
|
|
|
selectedIndex++;
|
|
|
|
|
|
|
|
if ((uint32_t) selectedIndex >= menu.size()) {
|
|
|
|
selectedIndex = menu.size() - 1;
|
2022-08-25 18:39:06 +02:00
|
|
|
}
|
2022-01-16 01:04:43 +01:00
|
|
|
redraw = true;
|
|
|
|
}
|
|
|
|
} else if (vpad.trigger & VPAD_BUTTON_A) {
|
|
|
|
break;
|
|
|
|
} else if (vpad.trigger & VPAD_BUTTON_X) {
|
2022-08-25 19:54:10 +02:00
|
|
|
autobootIndex = -1;
|
|
|
|
redraw = true;
|
2022-01-16 01:04:43 +01:00
|
|
|
} else if (vpad.trigger & VPAD_BUTTON_Y) {
|
2022-08-25 19:54:10 +02:00
|
|
|
autobootIndex = selectedIndex;
|
|
|
|
redraw = true;
|
2022-01-16 01:04:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (redraw) {
|
|
|
|
DrawUtils::beginDraw();
|
|
|
|
DrawUtils::clear(COLOR_BACKGROUND);
|
|
|
|
|
|
|
|
// draw buttons
|
|
|
|
uint32_t index = 8 + 24 + 8 + 4;
|
2022-08-25 19:54:10 +02:00
|
|
|
for (uint32_t i = 0; i < menu.size(); i++) {
|
|
|
|
if (i == (uint32_t) selectedIndex) {
|
2022-01-16 01:04:43 +01:00
|
|
|
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
|
|
|
|
} else {
|
2022-08-25 19:54:10 +02:00
|
|
|
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, (i == (uint32_t) autobootIndex) ? COLOR_AUTOBOOT : COLOR_BORDER);
|
2022-01-16 01:04:43 +01:00
|
|
|
}
|
|
|
|
|
2022-08-25 19:54:10 +02:00
|
|
|
std::string curName = std::next(menu.begin(), i)->second;
|
|
|
|
|
2022-01-16 01:04:43 +01:00
|
|
|
DrawUtils::setFontSize(24);
|
2022-08-25 19:54:10 +02:00
|
|
|
DrawUtils::setFontColor((i == (uint32_t) autobootIndex) ? COLOR_AUTOBOOT : COLOR_TEXT);
|
|
|
|
DrawUtils::print(16 * 2, index + 8 + 24, curName.c_str());
|
2022-01-16 01:04:43 +01:00
|
|
|
index += 42 + 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
DrawUtils::setFontColor(COLOR_TEXT);
|
|
|
|
|
|
|
|
// draw top bar
|
|
|
|
DrawUtils::setFontSize(24);
|
|
|
|
DrawUtils::drawPNG(16, 2, icon_png);
|
2022-08-25 18:13:37 +02:00
|
|
|
DrawUtils::print(64 + 2, 6 + 24, "Boot Selector");
|
2022-01-16 01:04:43 +01:00
|
|
|
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
2023-01-11 10:48:58 +01:00
|
|
|
DrawUtils::setFontSize(16);
|
|
|
|
DrawUtils::print(SCREEN_WIDTH - 16, 6 + 24, AUTOBOOT_MODULE_VERSION AUTOBOOT_MODULE_VERSION_EXTRA, true);
|
2022-01-16 01:04:43 +01:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DrawUtils::beginDraw();
|
|
|
|
DrawUtils::clear(COLOR_BLACK);
|
|
|
|
DrawUtils::endDraw();
|
|
|
|
|
|
|
|
DrawUtils::deinitFont();
|
|
|
|
|
|
|
|
// Call GX2Init to shut down OSScreen
|
|
|
|
GX2Init(nullptr);
|
|
|
|
|
|
|
|
free(screenBuffer);
|
|
|
|
|
2022-08-29 01:01:11 +02:00
|
|
|
int32_t selected = -1;
|
|
|
|
int32_t autoboot = -1;
|
2022-08-25 19:54:10 +02:00
|
|
|
// convert index to key
|
2022-08-29 01:01:11 +02:00
|
|
|
if (selectedIndex >= 0 && (uint32_t) selectedIndex < menu.size()) {
|
2022-08-25 19:54:10 +02:00
|
|
|
selected = std::next(menu.begin(), selectedIndex)->first;
|
|
|
|
}
|
2022-08-29 01:01:11 +02:00
|
|
|
if (autobootIndex >= 0 && (uint32_t) autobootIndex < menu.size()) {
|
2022-08-25 19:54:10 +02:00
|
|
|
autoboot = std::next(menu.begin(), autobootIndex)->first;
|
|
|
|
}
|
|
|
|
|
2022-01-16 01:04:43 +01:00
|
|
|
if (autoboot != autobootOptionInput) {
|
|
|
|
writeAutobootOption(configPath, autoboot);
|
|
|
|
}
|
|
|
|
|
|
|
|
return selected;
|
2022-01-18 14:17:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<AccountInfo>> &data) {
|
|
|
|
auto screenBuffer = DrawUtils::InitOSScreen();
|
|
|
|
if (!screenBuffer) {
|
|
|
|
OSFatal("Failed to alloc memory for screen");
|
|
|
|
}
|
|
|
|
|
2022-02-03 14:01:11 +01:00
|
|
|
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
2022-01-18 14:17:15 +01:00
|
|
|
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
|
|
|
|
|
|
|
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
2023-01-05 17:43:20 +01:00
|
|
|
if (!DrawUtils::initFont()) {
|
|
|
|
OSFatal("Failed to init font");
|
|
|
|
}
|
2022-01-18 14:17:15 +01:00
|
|
|
|
|
|
|
int32_t selected = 0;
|
2022-02-03 14:01:11 +01:00
|
|
|
bool redraw = true;
|
2022-01-18 14:17:15 +01:00
|
|
|
while (true) {
|
|
|
|
VPADStatus vpad{};
|
|
|
|
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
|
|
|
|
|
|
|
if (vpad.trigger & VPAD_BUTTON_UP) {
|
|
|
|
if (selected > 0) {
|
|
|
|
selected--;
|
|
|
|
redraw = true;
|
|
|
|
}
|
|
|
|
} else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
2022-01-18 16:29:54 +01:00
|
|
|
if (selected < (int32_t) data.size() - 1) {
|
2022-01-18 14:17:15 +01:00
|
|
|
selected++;
|
|
|
|
redraw = true;
|
|
|
|
}
|
|
|
|
} else if (vpad.trigger & VPAD_BUTTON_A) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redraw) {
|
|
|
|
DrawUtils::beginDraw();
|
|
|
|
DrawUtils::clear(COLOR_BACKGROUND);
|
|
|
|
|
|
|
|
// draw buttons
|
|
|
|
uint32_t index = 8 + 24 + 8 + 4;
|
2022-02-03 14:01:11 +01:00
|
|
|
int32_t start = (selected / 5) * 5;
|
|
|
|
int32_t end = (start + 5) < (int32_t) data.size() ? (start + 5) : data.size();
|
2022-01-18 14:17:15 +01:00
|
|
|
for (int i = start; i < end; i++) {
|
|
|
|
auto &val = data[i];
|
|
|
|
if (val->miiImageSize > 0) {
|
|
|
|
// Draw Mii
|
2022-02-03 14:01:11 +01:00
|
|
|
auto width = 128;
|
|
|
|
auto height = 128;
|
2022-01-18 16:29:54 +01:00
|
|
|
auto target_height = 64u;
|
2022-02-03 14:01:11 +01:00
|
|
|
auto target_width = 64u;
|
|
|
|
auto xOffset = 20;
|
|
|
|
auto yOffset = index;
|
2022-01-18 14:17:15 +01:00
|
|
|
for (uint32_t y = 0; y < target_height; y++) {
|
|
|
|
for (uint32_t x = 0; x < target_width; x++) {
|
2022-02-03 14:01:11 +01:00
|
|
|
uint32_t col = (((x) *width / target_width) + ((target_height - y - 1) * height / target_height) * width) * 4;
|
2022-01-18 14:17:15 +01:00
|
|
|
uint32_t colVal = ((uint32_t *) &val->miiImageBuffer[col + 1])[0];
|
2022-02-03 13:03:38 +01:00
|
|
|
if (colVal == 0x00808080) { // Remove the green background.
|
2022-01-18 14:17:15 +01:00
|
|
|
DrawUtils::drawPixel(x + xOffset, y + yOffset, COLOR_BACKGROUND.r, COLOR_BACKGROUND.g, COLOR_BACKGROUND.b, COLOR_BACKGROUND.a);
|
|
|
|
} else {
|
|
|
|
DrawUtils::drawPixel(x + xOffset, y + yOffset, val->miiImageBuffer[col + 1], val->miiImageBuffer[col + 2], val->miiImageBuffer[col + 3], val->miiImageBuffer[col]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == selected) {
|
|
|
|
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 64, 4, COLOR_BORDER_HIGHLIGHTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
DrawUtils::setFontSize(24);
|
|
|
|
DrawUtils::setFontColor(COLOR_TEXT);
|
|
|
|
|
|
|
|
std::string finalStr = val->name + (val->isNetworkAccount ? (std::string(" (NNID: ") + val->accountId + ")") : "");
|
|
|
|
DrawUtils::print(72 + 16 * 2, index + 8 + 32, finalStr.c_str());
|
|
|
|
|
|
|
|
index += 72 + 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
DrawUtils::setFontColor(COLOR_TEXT);
|
|
|
|
|
|
|
|
// draw top bar
|
|
|
|
DrawUtils::setFontSize(24);
|
|
|
|
DrawUtils::print(16, 6 + 24, "Select your Account");
|
2022-02-03 14:01:11 +01:00
|
|
|
auto curPage = (selected / 5) + 1;
|
2022-01-18 14:17:15 +01:00
|
|
|
auto totalPages = data.size() % 5 == 0 ? data.size() / 5 : data.size() / 5 + 1;
|
|
|
|
DrawUtils::print(SCREEN_WIDTH - 50, 6 + 24, string_format("%d/%d", curPage, totalPages).c_str());
|
|
|
|
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
if (start > 0) {
|
|
|
|
DrawUtils::setFontSize(36);
|
|
|
|
DrawUtils::print(SCREEN_WIDTH - 30, 68, "\uE01B", true);
|
|
|
|
}
|
|
|
|
|
2022-01-18 16:29:54 +01:00
|
|
|
if (end < (int32_t) data.size()) {
|
2022-01-18 14:17:15 +01:00
|
|
|
DrawUtils::setFontSize(36);
|
|
|
|
DrawUtils::print(SCREEN_WIDTH - 30, SCREEN_HEIGHT - 40, "\uE01C", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
DrawUtils::endDraw();
|
|
|
|
|
|
|
|
redraw = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DrawUtils::beginDraw();
|
|
|
|
DrawUtils::clear(COLOR_BLACK);
|
|
|
|
DrawUtils::endDraw();
|
|
|
|
|
|
|
|
DrawUtils::deinitFont();
|
|
|
|
|
|
|
|
// Call GX2Init to shut down OSScreen
|
|
|
|
GX2Init(nullptr);
|
|
|
|
|
|
|
|
free(screenBuffer);
|
|
|
|
|
2022-02-03 14:01:11 +01:00
|
|
|
auto i = 0;
|
2022-01-18 14:17:15 +01:00
|
|
|
nn::act::SlotNo resultSlot = 0;
|
2022-02-02 19:57:14 +01:00
|
|
|
for (auto const &val : data) {
|
2022-01-18 14:17:15 +01:00
|
|
|
if (i == selected) {
|
|
|
|
resultSlot = val->slot;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return resultSlot;
|
2022-09-04 21:19:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void handleUpdateWarningScreen() {
|
|
|
|
FILE *f = fopen(UPDATE_SKIP_PATH, "r");
|
|
|
|
if (f) {
|
|
|
|
DEBUG_FUNCTION_LINE("Skipping update warning screen");
|
|
|
|
fclose(f);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto screenBuffer = DrawUtils::InitOSScreen();
|
|
|
|
if (!screenBuffer) {
|
|
|
|
OSFatal("Failed to alloc memory for screen");
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
|
|
|
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
|
|
|
|
|
|
|
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
2023-01-05 17:43:20 +01:00
|
|
|
if (!DrawUtils::initFont()) {
|
|
|
|
OSFatal("Failed to init font");
|
|
|
|
}
|
2022-09-04 21:19:54 +02:00
|
|
|
|
|
|
|
DrawUtils::beginDraw();
|
|
|
|
DrawUtils::clear(COLOR_BACKGROUND_WARN);
|
|
|
|
|
|
|
|
DrawUtils::setFontColor(COLOR_TEXT);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2023-01-11 10:49:34 +01:00
|
|
|
const char *message = "The update folder currently exists and is not a file.";
|
2022-09-04 21:19:54 +02:00
|
|
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 - 24, message, true);
|
|
|
|
message = "Your system might not be blocking updates properly!";
|
|
|
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 + 0, 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 + 24, 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 / \ue001 Don't show this again";
|
|
|
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
|
|
|
|
|
|
|
DrawUtils::endDraw();
|
|
|
|
|
|
|
|
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) {
|
2023-01-14 14:10:59 +01:00
|
|
|
// 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);
|
2022-09-04 21:19:54 +02:00
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DrawUtils::clear(COLOR_BLACK);
|
|
|
|
DrawUtils::endDraw();
|
|
|
|
|
|
|
|
DrawUtils::deinitFont();
|
|
|
|
|
|
|
|
free(screenBuffer);
|
|
|
|
}
|
2023-04-02 11:59:16 +02:00
|
|
|
|
|
|
|
bool handleDiscInsertScreen(uint64_t expectedTitleId, uint64_t *titleIdToLaunch) {
|
|
|
|
if (titleIdToLaunch == nullptr) {
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("titleIdToLaunch is NULL");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SYSCheckTitleExists(expectedTitleId)) {
|
|
|
|
*titleIdToLaunch = expectedTitleId;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto result = false;
|
|
|
|
auto screenBuffer = DrawUtils::InitOSScreen();
|
|
|
|
if (!screenBuffer) {
|
|
|
|
OSFatal("Failed to alloc memory for screen");
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
|
|
|
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
|
|
|
|
|
|
|
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
|
|
|
if (!DrawUtils::initFont()) {
|
|
|
|
OSFatal("Failed to init font");
|
|
|
|
}
|
|
|
|
DrawUtils::beginDraw();
|
|
|
|
DrawUtils::clear(COLOR_BACKGROUND);
|
|
|
|
DrawUtils::endDraw();
|
|
|
|
|
|
|
|
uint64_t titleIdOfDisc = 0;
|
|
|
|
bool discInserted;
|
|
|
|
|
|
|
|
uint32_t attempt = 0;
|
|
|
|
while (!GetTitleIdOfDisc(&titleIdOfDisc, &discInserted)) {
|
|
|
|
if (++attempt > 20) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OSSleepTicks(OSMillisecondsToTicks(100));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wrongDiscInserted = discInserted && (titleIdOfDisc != expectedTitleId);
|
|
|
|
|
|
|
|
if (discInserted && !wrongDiscInserted) {
|
|
|
|
*titleIdToLaunch = expectedTitleId;
|
|
|
|
DrawUtils::deinitFont();
|
|
|
|
free(screenBuffer);
|
|
|
|
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.
|
|
|
|
bool allowDisc = !wrongDiscInserted;
|
|
|
|
while (true) {
|
|
|
|
VPADStatus vpad{};
|
|
|
|
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
|
|
|
|
|
|
|
if (vpad.trigger & VPAD_BUTTON_A) {
|
|
|
|
result = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
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::clear(COLOR_BLACK);
|
|
|
|
DrawUtils::endDraw();
|
|
|
|
|
|
|
|
DrawUtils::deinitFont();
|
|
|
|
|
|
|
|
free(screenBuffer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|