Implement new ConfigAPI and new Config Menu

This commit is contained in:
Maschell
2023-12-16 17:36:57 +01:00
parent b991d6e329
commit 921b5ce157
40 changed files with 2578 additions and 1319 deletions

View File

@@ -1,762 +0,0 @@
#include "ConfigUtils.h"
#include "../config/WUPSConfig.h"
#include "../globals.h"
#include "DrawUtils.h"
#include "hooks.h"
#include "logger.h"
#include <coreinit/screen.h>
#include <gx2/display.h>
#include <memory/mappedmemory.h>
#include <padscore/kpad.h>
#include <string>
#include <vector>
#include <vpad/input.h>
#define COLOR_BACKGROUND Color(238, 238, 238, 255)
#define COLOR_TEXT Color(51, 51, 51, 255)
#define COLOR_TEXT2 Color(72, 72, 72, 255)
#define COLOR_DISABLED Color(255, 0, 0, 255)
#define COLOR_BORDER Color(204, 204, 204, 255)
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4FF)
#define COLOR_WHITE Color(0xFFFFFFFF)
#define COLOR_BLACK Color(0, 0, 0, 255)
struct ConfigDisplayItem {
WUPSConfig *config{};
std::string name;
std::string author;
std::string version;
};
#define MAX_BUTTONS_ON_SCREEN 8
static uint32_t remapWiiMoteButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_BUTTON_LEFT)
conv_buttons |= VPAD_BUTTON_LEFT;
if (buttons & WPAD_BUTTON_RIGHT)
conv_buttons |= VPAD_BUTTON_RIGHT;
if (buttons & WPAD_BUTTON_DOWN)
conv_buttons |= VPAD_BUTTON_DOWN;
if (buttons & WPAD_BUTTON_UP)
conv_buttons |= VPAD_BUTTON_UP;
if (buttons & WPAD_BUTTON_PLUS)
conv_buttons |= VPAD_BUTTON_PLUS;
if (buttons & WPAD_BUTTON_B)
conv_buttons |= VPAD_BUTTON_B;
if (buttons & WPAD_BUTTON_A)
conv_buttons |= VPAD_BUTTON_A;
if (buttons & WPAD_BUTTON_MINUS)
conv_buttons |= VPAD_BUTTON_MINUS;
if (buttons & WPAD_BUTTON_HOME)
conv_buttons |= VPAD_BUTTON_HOME;
return conv_buttons;
}
static uint32_t remapClassicButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_CLASSIC_BUTTON_LEFT)
conv_buttons |= VPAD_BUTTON_LEFT;
if (buttons & WPAD_CLASSIC_BUTTON_RIGHT)
conv_buttons |= VPAD_BUTTON_RIGHT;
if (buttons & WPAD_CLASSIC_BUTTON_DOWN)
conv_buttons |= VPAD_BUTTON_DOWN;
if (buttons & WPAD_CLASSIC_BUTTON_UP)
conv_buttons |= VPAD_BUTTON_UP;
if (buttons & WPAD_CLASSIC_BUTTON_PLUS)
conv_buttons |= VPAD_BUTTON_PLUS;
if (buttons & WPAD_CLASSIC_BUTTON_X)
conv_buttons |= VPAD_BUTTON_X;
if (buttons & WPAD_CLASSIC_BUTTON_Y)
conv_buttons |= VPAD_BUTTON_Y;
if (buttons & WPAD_CLASSIC_BUTTON_B)
conv_buttons |= VPAD_BUTTON_B;
if (buttons & WPAD_CLASSIC_BUTTON_A)
conv_buttons |= VPAD_BUTTON_A;
if (buttons & WPAD_CLASSIC_BUTTON_MINUS)
conv_buttons |= VPAD_BUTTON_MINUS;
if (buttons & WPAD_CLASSIC_BUTTON_HOME)
conv_buttons |= VPAD_BUTTON_HOME;
if (buttons & WPAD_CLASSIC_BUTTON_ZR)
conv_buttons |= VPAD_BUTTON_ZR;
if (buttons & WPAD_CLASSIC_BUTTON_ZL)
conv_buttons |= VPAD_BUTTON_ZL;
if (buttons & WPAD_CLASSIC_BUTTON_R)
conv_buttons |= VPAD_BUTTON_R;
if (buttons & WPAD_CLASSIC_BUTTON_L)
conv_buttons |= VPAD_BUTTON_L;
return conv_buttons;
}
void ConfigUtils::displayMenu() {
renderBasicScreen("Loading configs...");
std::vector<ConfigDisplayItem> configs;
for (auto &plugin : gLoadedPlugins) {
ConfigDisplayItem cfg;
cfg.name = plugin->getMetaInformation().getName();
cfg.author = plugin->getMetaInformation().getAuthor();
cfg.version = plugin->getMetaInformation().getVersion();
for (const auto &hook : plugin->getPluginInformation().getHookDataList()) {
if (hook->getType() == WUPS_LOADER_HOOK_GET_CONFIG /*WUPS_LOADER_HOOK_GET_CONFIG*/) {
if (hook->getFunctionPointer() == nullptr) {
break;
}
auto *cur_config = reinterpret_cast<WUPSConfig *>(((WUPSConfigHandle(*)())((uint32_t *) hook->getFunctionPointer()))());
if (cur_config == nullptr) {
break;
}
cfg.config = cur_config;
configs.push_back(cfg);
break;
}
}
}
ConfigDisplayItem *currentConfig = nullptr;
WUPSConfigCategory *currentCategory = nullptr;
uint32_t selectedConfig = 0;
uint32_t selectedCat = 0;
uint32_t selectedItem = 0;
uint32_t start = 0;
uint32_t end = MAX_BUTTONS_ON_SCREEN;
if (configs.size() < MAX_BUTTONS_ON_SCREEN) {
end = configs.size();
}
bool redraw = true;
uint32_t buttonsTriggered;
uint32_t buttonsReleased;
VPADStatus vpad_data{};
VPADReadError vpad_error;
KPADStatus kpad_data{};
KPADError kpad_error;
int32_t prevSelectedItem = -1;
bool isItemMovementAllowed = true;
while (true) {
buttonsTriggered = 0;
buttonsReleased = 0;
VPADRead(VPAD_CHAN_0, &vpad_data, 1, &vpad_error);
if (vpad_error == VPAD_READ_SUCCESS) {
buttonsTriggered = vpad_data.trigger;
buttonsReleased = vpad_data.release;
}
for (int i = 0; i < 4; i++) {
if (KPADReadEx((KPADChan) i, &kpad_data, 1, &kpad_error) > 0) {
if (kpad_error == KPAD_ERROR_OK && kpad_data.extensionType != 0xFF) {
if (kpad_data.extensionType == WPAD_EXT_CORE || kpad_data.extensionType == WPAD_EXT_NUNCHUK) {
buttonsTriggered |= remapWiiMoteButtons(kpad_data.trigger);
buttonsReleased |= remapWiiMoteButtons(kpad_data.release);
} else {
buttonsTriggered |= remapClassicButtons(kpad_data.classic.trigger);
buttonsReleased |= remapClassicButtons(kpad_data.classic.release);
}
}
}
}
if (buttonsReleased & VPAD_BUTTON_HOME) {
break;
}
if (configs.empty()) {
renderBasicScreen("No configurable plugins loaded");
continue;
}
if (!currentConfig || !currentConfig->config) {
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
if (selectedConfig < configs.size() - 1) {
selectedCat = 0;
selectedConfig++;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_UP) {
if (selectedConfig > 0) {
selectedCat = 0;
selectedConfig--;
redraw = true;
}
}
if (buttonsTriggered & VPAD_BUTTON_A) {
currentConfig = &configs[selectedConfig];
if (currentConfig == nullptr) {
break;
}
selectedItem = 0;
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
auto cats = currentConfig->config->getCategories();
if (cats.size() < MAX_BUTTONS_ON_SCREEN) {
end = cats.size();
}
redraw = true;
continue;
}
if (selectedConfig >= end) {
end = selectedConfig + 1;
start = end - MAX_BUTTONS_ON_SCREEN;
} else if (selectedConfig < start) {
start = selectedConfig;
end = start + MAX_BUTTONS_ON_SCREEN;
}
if (redraw) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
// draw buttons
uint32_t index = 8 + 24 + 8 + 4;
for (uint32_t i = start; i < end; i++) {
DrawUtils::setFontColor(COLOR_TEXT);
if (i == selectedConfig) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, index + 8 + 24, configs[i].name.c_str());
uint32_t sz = DrawUtils::getTextWidth(configs[i].name.c_str());
DrawUtils::setFontSize(12);
DrawUtils::print(16 * 2 + sz + 4, index + 8 + 24, configs[i].author.c_str());
DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, configs[i].version.c_str(), true);
index += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < configs.size()) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
redraw = false;
}
continue;
}
if (!currentCategory) {
auto cats = currentConfig->config->getCategories();
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
if (selectedCat < cats.size() - 1) {
selectedItem = 0;
prevSelectedItem = 0;
selectedCat++;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_UP) {
if (selectedCat > 0) {
selectedItem = 0;
prevSelectedItem = 0;
selectedCat--;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_A) {
if (!cats.empty() && selectedCat > cats.size() - 1) {
selectedCat = 0;
}
currentCategory = cats[selectedCat];
if (currentCategory == nullptr) {
DEBUG_FUNCTION_LINE_ERR("currentCategory was NULL");
break;
}
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
prevSelectedItem = -1;
auto items = currentCategory->getItems();
if (items.size() < MAX_BUTTONS_ON_SCREEN) {
end = items.size();
}
redraw = true;
continue;
} else if (buttonsTriggered & VPAD_BUTTON_B) {
currentConfig = nullptr;
currentCategory = nullptr;
start = 0;
selectedItem = 0;
prevSelectedItem = 0;
end = MAX_BUTTONS_ON_SCREEN;
if (configs.size() < MAX_BUTTONS_ON_SCREEN) {
end = configs.size();
}
redraw = true;
continue;
}
if (selectedCat >= end) {
end = selectedCat + 1;
start = end - MAX_BUTTONS_ON_SCREEN;
} else if (selectedCat < start) {
start = selectedCat;
end = start + MAX_BUTTONS_ON_SCREEN;
}
if (redraw) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
// draw buttons
uint32_t index = 8 + 24 + 8 + 4;
for (uint32_t i = start; i < end; i++) {
DrawUtils::setFontColor(COLOR_TEXT);
if (i == selectedCat) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, index + 8 + 24, cats[i]->getName().c_str());
index += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(currentConfig->config->getName(), 45).c_str());
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < cats.size()) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
redraw = false;
}
continue;
}
const std::vector<WUPSConfigItem *> config_items = currentCategory->getItems();
if (config_items.empty()) {
if (buttonsTriggered & VPAD_BUTTON_B) {
currentCategory = nullptr;
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
auto catSize = currentConfig->config->getCategories().size();
if (catSize < MAX_BUTTONS_ON_SCREEN) {
end = catSize;
}
redraw = true;
continue;
}
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth("This category has no items");
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), "This category has no items");
auto headline = string_format("%s - %s", currentConfig->config->getName().c_str(), currentCategory->getName().c_str());
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(headline, 45).c_str());
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
// draw scroll indicator
DrawUtils::setFontSize(24);
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
continue;
}
if (isItemMovementAllowed) {
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
if (selectedItem < config_items.size() - 1) {
selectedItem++;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_UP) {
if (selectedItem > 0) {
selectedItem--;
redraw = true;
}
} else if (buttonsTriggered & VPAD_BUTTON_B) {
currentCategory = nullptr;
start = 0;
end = MAX_BUTTONS_ON_SCREEN;
auto catSize = currentConfig->config->getCategories().size();
if (catSize < MAX_BUTTONS_ON_SCREEN) {
end = catSize;
}
redraw = true;
continue;
}
}
WUPSConfigButtons pressedButtons = WUPS_CONFIG_BUTTON_NONE;
if (buttonsTriggered & VPAD_BUTTON_A) {
pressedButtons |= WUPS_CONFIG_BUTTON_A;
}
if (buttonsTriggered & VPAD_BUTTON_LEFT) {
pressedButtons |= WUPS_CONFIG_BUTTON_LEFT;
}
if (buttonsTriggered & VPAD_BUTTON_RIGHT) {
pressedButtons |= WUPS_CONFIG_BUTTON_RIGHT;
}
if (buttonsTriggered & VPAD_BUTTON_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_L;
}
if (buttonsTriggered & VPAD_BUTTON_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_R;
}
if (buttonsTriggered & VPAD_BUTTON_ZL) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZL;
}
if (buttonsTriggered & VPAD_BUTTON_ZR) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZR;
}
if (buttonsTriggered & VPAD_BUTTON_X) {
pressedButtons |= WUPS_CONFIG_BUTTON_Y;
}
if (buttonsTriggered & VPAD_BUTTON_Y) {
pressedButtons |= WUPS_CONFIG_BUTTON_X;
}
if (buttonsTriggered & VPAD_BUTTON_STICK_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_L;
}
if (buttonsTriggered & VPAD_BUTTON_STICK_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_R;
}
if (buttonsTriggered & VPAD_BUTTON_PLUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_PLUS;
}
if (buttonsTriggered & VPAD_BUTTON_MINUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_MINUS;
}
if (!isItemMovementAllowed) {
if (buttonsTriggered & VPAD_BUTTON_B) {
pressedButtons |= WUPS_CONFIG_BUTTON_B;
}
if (buttonsTriggered & VPAD_BUTTON_UP) {
pressedButtons |= WUPS_CONFIG_BUTTON_UP;
}
if (buttonsTriggered & VPAD_BUTTON_DOWN) {
pressedButtons |= WUPS_CONFIG_BUTTON_UP;
}
}
if (pressedButtons != WUPS_CONFIG_BUTTON_NONE) {
redraw = true;
}
if (selectedItem >= end) {
end = selectedItem + 1;
start = selectedItem - MAX_BUTTONS_ON_SCREEN;
} else if (selectedItem < start) {
start = selectedItem;
end = start + MAX_BUTTONS_ON_SCREEN;
}
if (redraw) {
if (prevSelectedItem != (int32_t) selectedItem) {
if (prevSelectedItem >= 0) {
config_items[prevSelectedItem]->onSelected(false);
}
config_items[selectedItem]->onSelected(true);
prevSelectedItem = (int32_t) selectedItem;
}
if (pressedButtons != WUPS_CONFIG_BUTTON_NONE) {
config_items[selectedItem]->onButtonPressed(pressedButtons);
}
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
// draw buttons
uint32_t index = 8 + 24 + 8 + 4;
for (uint32_t i = start; i < end; i++) {
DrawUtils::setFontColor(COLOR_TEXT);
if (i == selectedItem) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, index + 8 + 24, config_items[i]->getDisplayName().c_str());
if (i == selectedItem) {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, config_items[i]->getCurrentValueSelectedDisplay().c_str(), true);
} else {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, index + 8 + 24, config_items[i]->getCurrentValueDisplay().c_str(), true);
}
index += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
auto headline = string_format("%s - %s", currentConfig->config->getName().c_str(), currentCategory->getName().c_str());
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(headline, 45).c_str());
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, currentConfig->version.c_str(), true);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 / \ue07e Toggle", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < config_items.size()) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
redraw = pressedButtons != WUPS_CONFIG_BUTTON_NONE;
isItemMovementAllowed = config_items[selectedItem]->isMovementAllowed();
}
}
for (const auto &element : configs) {
for (const auto &cat : element.config->getCategories()) {
for (const auto &item : cat->getItems()) {
if (item->isDirty()) {
item->callCallback();
}
}
}
}
CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_CONFIG_CLOSED);
for (const auto &element : configs) {
delete element.config;
}
}
#define __SetDCPitchReg ((void (*)(uint32_t, uint32_t))(0x101C400 + 0x1e714))
void ConfigUtils::openConfigMenu() {
bool wasHomeButtonMenuEnabled = OSIsHomeButtonMenuEnabled();
OSScreenInit();
uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV);
uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC);
void *screenbuffer0 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size, 0x100);
void *screenbuffer1 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf1_size, 0x100);
// Fix the TV buffer pitch if a 1080p buffer is used.
if (screen_buf0_size == 0x00FD2000) {
__SetDCPitchReg(SCREEN_TV, 1920);
}
bool skipScreen0Free = false;
bool skipScreen1Free = false;
if (!screenbuffer0 || !screenbuffer1) {
if (screenbuffer0 == nullptr) {
if (gStoredTVBuffer.buffer_size >= screen_buf0_size) {
screenbuffer0 = gStoredTVBuffer.buffer;
skipScreen0Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedTVBuffer");
}
}
if (screenbuffer1 == nullptr) {
if (gStoredDRCBuffer.buffer_size >= screen_buf1_size) {
screenbuffer1 = gStoredDRCBuffer.buffer;
skipScreen1Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedDRCBuffer");
}
}
if (!screenbuffer0 || !screenbuffer1) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc buffers");
goto error_exit;
}
}
OSScreenSetBufferEx(SCREEN_TV, screenbuffer0);
OSScreenSetBufferEx(SCREEN_DRC, screenbuffer1);
OSScreenEnableEx(SCREEN_TV, 1);
OSScreenEnableEx(SCREEN_DRC, 1);
// Clear screens
OSScreenClearBufferEx(SCREEN_TV, 0);
OSScreenClearBufferEx(SCREEN_DRC, 0);
// Flip buffers
OSScreenFlipBuffersEx(SCREEN_TV);
OSScreenFlipBuffersEx(SCREEN_DRC);
DrawUtils::initBuffers(screenbuffer0, screen_buf0_size, screenbuffer1, screen_buf1_size);
if (!DrawUtils::initFont()) {
DEBUG_FUNCTION_LINE_ERR("Failed to init Font");
goto error_exit;
}
// disable the home button menu to prevent opening it when exiting
OSEnableHomeButtonMenu(false);
displayMenu();
OSEnableHomeButtonMenu(wasHomeButtonMenuEnabled);
DrawUtils::deinitFont();
error_exit:
if (gStoredTVBuffer.buffer != nullptr) {
GX2SetTVBuffer(gStoredTVBuffer.buffer, gStoredTVBuffer.buffer_size, static_cast<GX2TVRenderMode>(gStoredTVBuffer.mode),
gStoredTVBuffer.surface_format, gStoredTVBuffer.buffering_mode);
}
if (gStoredDRCBuffer.buffer != nullptr) {
GX2SetDRCBuffer(gStoredDRCBuffer.buffer, gStoredDRCBuffer.buffer_size, static_cast<GX2DrcRenderMode>(gStoredDRCBuffer.mode),
gStoredDRCBuffer.surface_format, gStoredDRCBuffer.buffering_mode);
}
if (!skipScreen0Free && screenbuffer0) {
MEMFreeToMappedMemory(screenbuffer0);
}
if (!skipScreen1Free && screenbuffer1) {
MEMFreeToMappedMemory(screenbuffer1);
}
}
void ConfigUtils::renderBasicScreen(std::string_view text) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth(text.data());
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), text.data());
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <gx2/enum.h>
#include <string>
struct StoredBuffer {
void *buffer;
uint32_t buffer_size;
uint32_t mode;
GX2SurfaceFormat surface_format;
GX2BufferingMode buffering_mode;
};
class ConfigUtils {
public:
static void openConfigMenu();
private:
static void displayMenu();
static void renderBasicScreen(std::string_view text);
};

View File

@@ -0,0 +1,221 @@
#include "CategoryRenderer.h"
#include "ConfigDefines.h"
#include "config/WUPSConfigCategory.h"
#include "utils/input/Input.h"
#include "utils/utils.h"
#include <vector>
#include <wups/config.h>
CategoryRenderer::CategoryRenderer(const GeneralConfigInformation *info, const WUPSConfigAPIBackend::WUPSConfigCategory *cat, bool isRoot)
: mInfo(info), mCat(cat), mIsRoot(isRoot) {
for (uint32_t i = 0; i < cat->getCategories().size() + cat->getItems().size(); i++) {
if (i < cat->getCategories().size()) {
auto item = make_unique_nothrow<ConfigRendererItemCategory>(cat->getCategories()[i].get());
if (!item) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc ConfigRendererItemCategory");
OSFatal("WiiUPluginBackend::CategoryRenderer::CategoryRenderer() Failed to alloc ConfigRendererItemCategory");
}
mItemRenderer.push_back(std::move(item));
} else {
auto itemIndex = (int32_t) (i - cat->getCategories().size());
if (itemIndex < 0 || itemIndex >= (int32_t) cat->getItems().size()) {
DEBUG_FUNCTION_LINE_ERR("unexpected index");
OSFatal("WiiUPluginBackend::CategoryRenderer::CategoryRenderer() unexpected index");
}
auto item = make_unique_nothrow<ConfigRendererItem>(cat->getItems()[itemIndex].get());
if (!item) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc ConfigRendererItemCategory");
OSFatal("WiiUPluginBackend::CategoryRenderer::CategoryRenderer() Failed to alloc ConfigRendererItem");
}
mItemRenderer.push_back(std::move(item));
}
}
mCursorPos = 0;
if (!mItemRenderer.empty()) {
mItemRenderer[mCursorPos]->SetIsSelected(true);
}
}
CategoryRenderer::~CategoryRenderer() {
if (mCursorPos < (int32_t) mItemRenderer.size()) {
mItemRenderer[mCursorPos]->SetIsSelected(false);
}
}
ConfigSubState CategoryRenderer::Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData) {
switch (mState) {
case STATE_MAIN: {
auto res = UpdateStateMain(input, simpleInputData, complexInputData);
mFirstFrame = false;
return res;
}
case STATE_SUB: {
if (mSubCategoryRenderer) {
auto subResult = mSubCategoryRenderer->Update(input, simpleInputData, complexInputData);
if (subResult != SUB_STATE_RUNNING) {
mState = STATE_MAIN;
mFirstFrame = true;
return SUB_STATE_RUNNING;
}
return SUB_STATE_RUNNING;
} else {
DEBUG_FUNCTION_LINE_WARN("State is RENDERER_STATE_CAT but mCategoryRenderer is null. Resetting state.");
mState = STATE_MAIN;
mCursorPos = 0;
}
}
}
return SUB_STATE_ERROR;
}
ConfigSubState CategoryRenderer::UpdateStateMain(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData) {
if (mIsItemMovementAllowed && input.data.buttons_d & Input::eButtons::BUTTON_B) {
return SUB_STATE_RETURN;
}
if (mItemRenderer.empty()) {
return SUB_STATE_RUNNING;
}
auto totalElementSize = mItemRenderer.size();
int32_t prevSelectedItem = mCursorPos;
if (mIsItemMovementAllowed) {
if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) {
mCursorPos++;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) {
mCursorPos--;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
if (mCursorPos < (int32_t) mCat->getCategories().size()) {
if (mCurrentOpen != mCursorPos) {
mSubCategoryRenderer.reset();
mSubCategoryRenderer = make_unique_nothrow<CategoryRenderer>(mInfo, mCat->getCategories()[mCursorPos].get(), false);
}
mCurrentOpen = mCursorPos;
mState = STATE_SUB;
}
}
}
if (mCursorPos < 0) {
mCursorPos = (int32_t) totalElementSize - 1;
} else if (mCursorPos > (int32_t) (totalElementSize - 1)) {
mCursorPos = 0;
}
// Adjust the render offset when reaching the boundaries
if (mCursorPos < mRenderOffset) {
mRenderOffset = mCursorPos;
} else if (mCursorPos >= mRenderOffset + MAX_BUTTONS_ON_SCREEN - 1) {
mRenderOffset = mCursorPos - MAX_BUTTONS_ON_SCREEN + 1;
}
bool posJustChanged = false;
if (prevSelectedItem != mCursorPos) {
mItemRenderer[prevSelectedItem]->SetIsSelected(false);
mItemRenderer[mCursorPos]->SetIsSelected(true);
posJustChanged = true;
}
if (!posJustChanged && !mFirstFrame) {
// WUPSConfigItemV2
mItemRenderer[mCursorPos]->OnInput(simpleInputData);
mItemRenderer[mCursorPos]->OnInputEx(complexInputData);
// WUPSConfigItemV1
if (input.data.buttons_d != 0) {
WUPSConfigButtons buttons = input.data.buttons_d;
buttons = ConfigUtils::convertInputs(buttons);
if (!mIsItemMovementAllowed) {
buttons &= ~MOVE_ITEM_INPUT_MASK;
}
mItemRenderer[mCursorPos]->OnButtonPressed(buttons);
}
}
mIsItemMovementAllowed = mItemRenderer[mCursorPos]->IsMovementAllowed();
return SUB_STATE_RUNNING;
}
void CategoryRenderer::Render() const {
switch (mState) {
case STATE_MAIN:
RenderStateMain();
break;
case STATE_SUB: {
if (mSubCategoryRenderer) {
mSubCategoryRenderer->Render();
} else {
DEBUG_FUNCTION_LINE_WARN("STATE_SUB but mSubCategoryRenderer is NULL");
}
break;
}
}
}
void CategoryRenderer::RenderStateMain() const {
if (mItemRenderer.empty()) {
DrawUtils::beginDraw();
RenderMainLayout();
std::string text(mIsRoot ? "This plugin can not be configured" : "This category is empty");
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth(text.c_str());
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), text.c_str());
DrawUtils::endDraw();
return;
}
auto totalElementSize = static_cast<int>(mItemRenderer.size());
// Calculate the range of items to display
int start = std::max(0, mRenderOffset);
int end = std::min(start + MAX_BUTTONS_ON_SCREEN, totalElementSize);
DrawUtils::beginDraw();
RenderMainLayout();
uint32_t yOffset = 8 + 24 + 8 + 4;
for (int32_t i = start; i < end; i++) {
bool isHighlighted = (i == mCursorPos);
mItemRenderer[i]->Draw(yOffset, isHighlighted);
yOffset += 42 + 8;
}
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < totalElementSize) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
DrawUtils::endDraw();
}
void CategoryRenderer::RenderMainLayout() const {
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, StringTools::truncate(mInfo->name, 45).c_str());
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, mInfo->version.c_str(), true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue001 Back";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "../DrawUtils.h"
#include "ConfigRendererItem.h"
#include "ConfigRendererItemCategory.h"
#include "ConfigRendererItemGeneric.h"
#include "ConfigUtils.h"
#include "config/WUPSConfigCategory.h"
#include "utils/input/Input.h"
#include <memory>
class CategoryRenderer {
public:
explicit CategoryRenderer(const GeneralConfigInformation *info, const WUPSConfigAPIBackend::WUPSConfigCategory *cat, bool isRoot);
~CategoryRenderer();
ConfigSubState Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData);
void Render() const;
private:
ConfigSubState UpdateStateMain(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData);
void RenderStateMain() const;
void RenderMainLayout() const;
enum State {
STATE_MAIN = 0,
STATE_SUB = 1,
};
State mState = STATE_MAIN;
int32_t mCurrentOpen = -1;
int32_t mCursorPos = 0;
int32_t mRenderOffset = 0;
std::unique_ptr<CategoryRenderer> mSubCategoryRenderer = {};
const GeneralConfigInformation *mInfo = {};
const WUPSConfigAPIBackend::WUPSConfigCategory *mCat = {};
std::vector<std::unique_ptr<ConfigRendererItemGeneric>> mItemRenderer = {};
bool mIsItemMovementAllowed = true;
bool mFirstFrame = true;
bool mIsRoot = false;
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include <gx2/surface.h>
#include <string>
#define COLOR_BACKGROUND Color(238, 238, 238, 255)
#define COLOR_TEXT Color(51, 51, 51, 255)
#define COLOR_TEXT2 Color(72, 72, 72, 255)
#define COLOR_DISABLED Color(255, 0, 0, 255)
#define COLOR_BORDER Color(204, 204, 204, 255)
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4FF)
#define COLOR_WHITE Color(0xFFFFFFFF)
#define COLOR_BLACK Color(0, 0, 0, 255)
#define MAX_BUTTONS_ON_SCREEN 8
struct StoredBuffer {
void *buffer;
uint32_t buffer_size;
uint32_t mode;
GX2SurfaceFormat surface_format;
GX2BufferingMode buffering_mode;
};
enum ConfigSubState {
SUB_STATE_RUNNING = 0,
SUB_STATE_RETURN = 1,
SUB_STATE_ERROR = 2,
};

View File

@@ -0,0 +1,26 @@
#pragma once
#include "config/WUPSConfig.h"
#include <memory>
struct GeneralConfigInformation {
std::string name;
std::string author;
std::string version;
};
class ConfigDisplayItem {
public:
ConfigDisplayItem(GeneralConfigInformation &info, std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config) : mConfig(std::move(config)), mInfo(std::move(info)) {
assert(mConfig);
}
[[nodiscard]] const GeneralConfigInformation &getConfigInformation() const {
return mInfo;
}
[[nodiscard]] const WUPSConfigAPIBackend::WUPSConfig &getConfig() const {
return *mConfig;
}
private:
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> mConfig;
GeneralConfigInformation mInfo;
};

View File

@@ -0,0 +1,154 @@
#include "ConfigRenderer.h"
void ConfigRenderer::RenderStateMain() const {
auto totalElementSize = (int32_t) mConfigs.size();
// Calculate the range of items to display
int start = std::max(0, mRenderOffset);
int end = std::min(start + MAX_BUTTONS_ON_SCREEN, totalElementSize);
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
uint32_t yOffset = 8 + 24 + 8 + 4;
for (int32_t i = start; i < end; i++) {
drawConfigEntry(yOffset, mConfigs[i].getConfigInformation(), i == mCursorPos);
yOffset += 42 + 8;
}
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
// draw scroll indicator
DrawUtils::setFontSize(24);
if (end < totalElementSize) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
}
if (start > 0) {
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
}
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
}
void ConfigRenderer::drawConfigEntry(uint32_t yOffset, const GeneralConfigInformation &configInformation, bool isHighlighted) const {
DrawUtils::setFontColor(COLOR_TEXT);
if (isHighlighted) {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::print(16 * 2, yOffset + 8 + 24, configInformation.name.c_str());
uint32_t sz = DrawUtils::getTextWidth(configInformation.name.c_str());
DrawUtils::setFontSize(12);
DrawUtils::print(16 * 2 + sz + 4, yOffset + 8 + 24, configInformation.author.c_str());
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, configInformation.version.c_str(), true);
}
ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) {
if (mConfigs.empty()) {
return SUB_STATE_ERROR;
}
auto totalElementSize = mConfigs.size();
if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) {
mCursorPos++;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) {
mCursorPos--;
} else if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
if (mCursorPos != mCurrentOpen) {
mCategoryRenderer.reset();
mCategoryRenderer = make_unique_nothrow<CategoryRenderer>(&(mConfigs[mCursorPos].getConfigInformation()), &(mConfigs[mCursorPos].getConfig()), true);
}
mCurrentOpen = mCursorPos;
mState = STATE_SUB;
return SUB_STATE_RUNNING;
} else if (input.data.buttons_d & (Input::eButtons::BUTTON_B | Input::eButtons::BUTTON_HOME)) {
mCategoryRenderer.reset();
for (const auto &element : mConfigs) {
CallOnCloseCallback(element.getConfigInformation(), element.getConfig().getCategories());
}
return SUB_STATE_RETURN;
}
if (mCursorPos < 0) {
mCursorPos = (int32_t) totalElementSize - 1;
} else if (mCursorPos > (int32_t) (totalElementSize - 1)) {
mCursorPos = 0;
}
// Adjust the render offset when reaching the boundaries
if (mCursorPos < mRenderOffset) {
mRenderOffset = mCursorPos;
} else if (mCursorPos >= mRenderOffset + MAX_BUTTONS_ON_SCREEN - 1) {
mRenderOffset = mCursorPos - MAX_BUTTONS_ON_SCREEN + 1;
}
return SUB_STATE_RUNNING;
}
void ConfigRenderer::Render() const {
switch (mState) {
case STATE_MAIN:
RenderStateMain();
break;
case STATE_SUB: {
if (mCategoryRenderer) {
mCategoryRenderer->Render();
} else {
DEBUG_FUNCTION_LINE_WARN("render failed: state was RENDERER_STATE_CAT but mCategoryRenderer is NULL");
}
break;
}
}
}
ConfigSubState ConfigRenderer::Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData) {
switch (mState) {
case STATE_MAIN:
return UpdateStateMain(input);
case STATE_SUB: {
if (mCategoryRenderer) {
auto subResult = mCategoryRenderer->Update(input, simpleInputData, complexInputData);
if (subResult != SUB_STATE_RUNNING) {
mState = STATE_MAIN;
return SUB_STATE_RUNNING;
}
return SUB_STATE_RUNNING;
} else {
DEBUG_FUNCTION_LINE_WARN("State is RENDERER_STATE_CAT but mCategoryRenderer is null. Resetting state.");
mState = STATE_MAIN;
mCursorPos = 0;
}
}
}
return SUB_STATE_ERROR;
}
void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories) {
for (const auto &cat : categories) {
if (!cat->getCategories().empty()) {
CallOnCloseCallback(info, cat->getCategories());
}
for (const auto &item : cat->getItems()) {
item->onCloseCallback();
}
}
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include "../DrawUtils.h"
#include "../input/Input.h"
#include "../logger.h"
#include "CategoryRenderer.h"
#include "globals.h"
#include <memory>
#include <vector>
class ConfigRenderer {
public:
explicit ConfigRenderer(std::vector<ConfigDisplayItem> &&vec) : mConfigs(std::move(vec)) {
}
~ConfigRenderer() = default;
ConfigSubState Update(Input &input, const WUPSConfigSimplePadData &simpleInputData, const WUPSConfigComplexPadData &complexInputData);
void Render() const;
private:
ConfigSubState UpdateStateMain(const Input &input);
void RenderStateMain() const;
void drawConfigEntry(uint32_t yOffset, const GeneralConfigInformation &configInformation, bool isHighlighted) const;
enum State {
STATE_MAIN = 0,
STATE_SUB = 1,
};
std::vector<ConfigDisplayItem> mConfigs;
std::unique_ptr<CategoryRenderer> mCategoryRenderer = {};
State mState = STATE_MAIN;
int32_t mCursorPos = 0;
int32_t mRenderOffset = 0;
int32_t mCurrentOpen = -1;
void CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories);
};

View File

@@ -0,0 +1,43 @@
#pragma once
#include "ConfigRendererItemGeneric.h"
#include "config/WUPSConfigItem.h"
class ConfigRendererItem : public ConfigRendererItemGeneric {
public:
explicit ConfigRendererItem(const WUPSConfigAPIBackend::WUPSConfigItem *item) : mItem(item) {
assert(item);
}
void Draw(uint32_t yOffset, bool isHighlighted) const override {
assert(mItem);
drawGenericBoxAndText(yOffset, mItem->getDisplayName(), isHighlighted);
DrawUtils::setFontSize(24);
if (isHighlighted) {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, mItem->getCurrentValueSelectedDisplay().c_str(), true);
} else {
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, mItem->getCurrentValueDisplay().c_str(), true);
}
}
void SetIsSelected(bool isSelected) override {
mItem->onSelected(isSelected);
}
void OnButtonPressed(WUPSConfigButtons buttons) override {
mItem->onButtonPressed(buttons);
}
[[nodiscard]] bool IsMovementAllowed() const override {
return mItem->isMovementAllowed();
}
void OnInput(WUPSConfigSimplePadData input) override {
mItem->onInput(input);
}
void OnInputEx(WUPSConfigComplexPadData input) override {
mItem->onInputEx(input);
}
private:
const WUPSConfigAPIBackend::WUPSConfigItem *mItem;
};

View File

@@ -0,0 +1,17 @@
#pragma once
#include "ConfigRendererItemGeneric.h"
#include "config/WUPSConfigCategory.h"
class ConfigRendererItemCategory : public ConfigRendererItemGeneric {
public:
explicit ConfigRendererItemCategory(const WUPSConfigAPIBackend::WUPSConfigCategory *category) : mCategory(category) {
assert(category);
}
void Draw(uint32_t yOffset, bool isHighlighted) const override {
drawGenericBoxAndText(yOffset, mCategory->getName(), isHighlighted);
}
private:
const WUPSConfigAPIBackend::WUPSConfigCategory *mCategory;
};

View File

@@ -0,0 +1,36 @@
#pragma once
#include "../DrawUtils.h"
#include "ConfigDefines.h"
#include <wups/config.h>
class ConfigRendererItemGeneric {
public:
virtual ~ConfigRendererItemGeneric() = default;
virtual void drawGenericBoxAndText(uint32_t yOffset, const std::string &displayName, bool isHighlighted) const {
if (isHighlighted) {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
} else {
DrawUtils::drawRect(16, yOffset, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
DrawUtils::setFontSize(24);
DrawUtils::setFontColor(COLOR_TEXT);
DrawUtils::print(16 * 2, yOffset + 8 + 24, displayName.c_str());
}
virtual void Draw(uint32_t yOffset, bool isHighlighted) const = 0;
virtual void SetIsSelected(bool) {
}
virtual void OnButtonPressed(WUPSConfigButtons) {
}
virtual void OnInput(WUPSConfigSimplePadData) {
}
virtual void OnInputEx(WUPSConfigComplexPadData) {
}
[[nodiscard]] virtual bool IsMovementAllowed() const {
return true;
}
};

View File

@@ -0,0 +1,308 @@
#include "ConfigUtils.h"
#include "../../globals.h"
#include "../DrawUtils.h"
#include "../logger.h"
#include "ConfigRenderer.h"
#include "config/WUPSConfigAPI.h"
#include "hooks.h"
#include "utils/input/CombinedInput.h"
#include "utils/input/VPADInput.h"
#include "utils/input/WPADInput.h"
#include <coreinit/screen.h>
#include <gx2/display.h>
#include <memory/mappedmemory.h>
#include <string>
#include <vector>
WUPS_CONFIG_SIMPLE_INPUT ConfigUtils::convertInputs(uint32_t buttons) {
WUPSConfigButtons pressedButtons = WUPS_CONFIG_BUTTON_NONE;
if (buttons & Input::eButtons::BUTTON_A) {
pressedButtons |= WUPS_CONFIG_BUTTON_A;
}
if (buttons & Input::eButtons::BUTTON_LEFT) {
pressedButtons |= WUPS_CONFIG_BUTTON_LEFT;
}
if (buttons & Input::eButtons::BUTTON_RIGHT) {
pressedButtons |= WUPS_CONFIG_BUTTON_RIGHT;
}
if (buttons & Input::eButtons::BUTTON_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_L;
}
if (buttons & Input::eButtons::BUTTON_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_R;
}
if (buttons & Input::eButtons::BUTTON_ZL) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZL;
}
if (buttons & Input::eButtons::BUTTON_ZR) {
pressedButtons |= WUPS_CONFIG_BUTTON_ZR;
}
if (buttons & Input::eButtons::BUTTON_X) {
pressedButtons |= WUPS_CONFIG_BUTTON_X;
}
if (buttons & Input::eButtons::BUTTON_Y) {
pressedButtons |= WUPS_CONFIG_BUTTON_Y;
}
if (buttons & Input::eButtons::BUTTON_STICK_L) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_L;
}
if (buttons & Input::eButtons::BUTTON_STICK_R) {
pressedButtons |= WUPS_CONFIG_BUTTON_STICK_R;
}
if (buttons & Input::eButtons::BUTTON_PLUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_PLUS;
}
if (buttons & Input::eButtons::BUTTON_MINUS) {
pressedButtons |= WUPS_CONFIG_BUTTON_MINUS;
}
if (buttons & Input::eButtons::BUTTON_B) {
pressedButtons |= WUPS_CONFIG_BUTTON_B;
}
if (buttons & Input::eButtons::BUTTON_UP) {
pressedButtons |= WUPS_CONFIG_BUTTON_UP;
}
if (buttons & Input::eButtons::BUTTON_DOWN) {
pressedButtons |= WUPS_CONFIG_BUTTON_DOWN;
}
return (WUPS_CONFIG_SIMPLE_INPUT) pressedButtons;
}
void ConfigUtils::displayMenu() {
renderBasicScreen("Loading configs...");
std::vector<ConfigDisplayItem> configs;
for (auto &plugin : gLoadedPlugins) {
GeneralConfigInformation info;
info.name = plugin->getMetaInformation().getName();
info.author = plugin->getMetaInformation().getAuthor();
info.version = plugin->getMetaInformation().getVersion();
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config;
auto configData = plugin->getConfigData();
if (configData) {
auto configHandleOpt = configData->createConfig();
if (configHandleOpt) {
WUPSConfigAPIStatus callbackResult = configData->CallMenuOpenendCallback(configHandleOpt.value());
config = WUPSConfigAPIBackend::Intern::PopConfigByHandle(configHandleOpt.value());
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to get config for handle: %08X", configHandleOpt.value().handle);
} else if (callbackResult != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Callback failed for %s: %s", info.name.c_str(), WUPSConfigAPI_GetStatusStr(callbackResult));
config.reset();
}
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to create config for plugin: \"%s\"", info.name.c_str());
}
} else {
for (const auto &hook : plugin->getPluginInformation().getHookDataList()) {
if (hook->getType() == WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED) {
if (hook->getFunctionPointer() == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Hook had invalid ptr");
break;
}
auto cur_config_handle = ((void *(*) ())((uint32_t *) hook->getFunctionPointer()))();
if (cur_config_handle == nullptr) {
DEBUG_FUNCTION_LINE_WARN("Hook returned empty handle");
break;
}
config = WUPSConfigAPIBackend::Intern::PopConfigByHandle(WUPSConfigHandle(cur_config_handle));
if (!config) {
DEBUG_FUNCTION_LINE_ERR("Failed to find config for handle: %08X", cur_config_handle);
}
break;
}
}
}
if (!config) {
config = make_unique_nothrow<WUPSConfigAPIBackend::WUPSConfig>("DUMMY");
}
configs.emplace_back(info, std::move(config));
}
ConfigRenderer renderer(std::move(configs));
configs.clear();
CombinedInput baseInput;
VPadInput vpadInput;
WPADInput wpadInputs[4] = {
WPAD_CHAN_0,
WPAD_CHAN_1,
WPAD_CHAN_2,
WPAD_CHAN_3};
auto startTime = OSGetTime();
while (true) {
baseInput.reset();
if (vpadInput.update(1280, 720)) {
baseInput.combine(vpadInput);
}
for (auto &wpadInput : wpadInputs) {
if (wpadInput.update(1280, 720)) {
baseInput.combine(wpadInput);
}
}
baseInput.process();
WUPSConfigSimplePadData simpleData;
simpleData.buttons_d = convertInputs(baseInput.data.buttons_d);
simpleData.buttons_r = convertInputs(baseInput.data.buttons_r);
simpleData.buttons_h = convertInputs(baseInput.data.buttons_h);
simpleData.x = baseInput.data.x;
simpleData.y = baseInput.data.y;
simpleData.touched = baseInput.data.touched;
simpleData.validPointer = baseInput.data.validPointer;
WUPSConfigComplexPadData complexData;
complexData.vpad.data = vpadInput.vpad;
complexData.vpad.tpCalib = vpadInput.tpCalib;
complexData.vpad.vpadError = vpadInput.vpadError;
for (int i = 0; i < 4; i++) {
complexData.kpad.kpadError[i] = wpadInputs[i].kpadError;
complexData.kpad.data[i] = wpadInputs[i].kpad;
}
auto subState = renderer.Update(baseInput, simpleData, complexData);
if (subState != SUB_STATE_RUNNING) {
break;
}
renderer.Render();
auto diffTime = OSTicksToMicroseconds(OSGetTime() - startTime);
if (diffTime < 16000) {
OSSleepTicks(OSMicrosecondsToTicks(16000 - diffTime));
}
}
for (auto &plugin : gLoadedPlugins) {
auto configData = plugin->getConfigData();
if (configData) {
configData->CallMenuClosedCallback();
} else {
CallHook(*plugin, WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED);
}
}
WUPSConfigAPIBackend::Intern::CleanAllHandles();
}
#define __SetDCPitchReg ((void (*)(uint32_t, uint32_t))(0x101C400 + 0x1e714))
void ConfigUtils::openConfigMenu() {
bool wasHomeButtonMenuEnabled = OSIsHomeButtonMenuEnabled();
OSScreenInit();
uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV);
uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC);
void *screenbuffer0 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size, 0x100);
void *screenbuffer1 = MEMAllocFromMappedMemoryForGX2Ex(screen_buf1_size, 0x100);
// Fix the TV buffer pitch if a 1080p buffer is used.
if (screen_buf0_size == 0x00FD2000) {
__SetDCPitchReg(SCREEN_TV, 1920);
}
bool skipScreen0Free = false;
bool skipScreen1Free = false;
if (!screenbuffer0 || !screenbuffer1) {
if (screenbuffer0 == nullptr) {
if (gStoredTVBuffer.buffer_size >= screen_buf0_size) {
screenbuffer0 = gStoredTVBuffer.buffer;
skipScreen0Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedTVBuffer");
}
}
if (screenbuffer1 == nullptr) {
if (gStoredDRCBuffer.buffer_size >= screen_buf1_size) {
screenbuffer1 = gStoredDRCBuffer.buffer;
skipScreen1Free = true;
DEBUG_FUNCTION_LINE_VERBOSE("Use storedDRCBuffer");
}
}
if (!screenbuffer0 || !screenbuffer1) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc buffers");
goto error_exit;
}
}
OSScreenSetBufferEx(SCREEN_TV, screenbuffer0);
OSScreenSetBufferEx(SCREEN_DRC, screenbuffer1);
OSScreenEnableEx(SCREEN_TV, 1);
OSScreenEnableEx(SCREEN_DRC, 1);
// Clear screens
OSScreenClearBufferEx(SCREEN_TV, 0);
OSScreenClearBufferEx(SCREEN_DRC, 0);
// Flip buffers
OSScreenFlipBuffersEx(SCREEN_TV);
OSScreenFlipBuffersEx(SCREEN_DRC);
DrawUtils::initBuffers(screenbuffer0, screen_buf0_size, screenbuffer1, screen_buf1_size);
if (!DrawUtils::initFont()) {
DEBUG_FUNCTION_LINE_ERR("Failed to init Font");
goto error_exit;
}
// disable the home button menu to prevent opening it when exiting
OSEnableHomeButtonMenu(false);
displayMenu();
OSEnableHomeButtonMenu(wasHomeButtonMenuEnabled);
DrawUtils::deinitFont();
error_exit:
if (gStoredTVBuffer.buffer != nullptr) {
GX2SetTVBuffer(gStoredTVBuffer.buffer, gStoredTVBuffer.buffer_size, static_cast<GX2TVRenderMode>(gStoredTVBuffer.mode),
gStoredTVBuffer.surface_format, gStoredTVBuffer.buffering_mode);
}
if (gStoredDRCBuffer.buffer != nullptr) {
GX2SetDRCBuffer(gStoredDRCBuffer.buffer, gStoredDRCBuffer.buffer_size, static_cast<GX2DrcRenderMode>(gStoredDRCBuffer.mode),
gStoredDRCBuffer.surface_format, gStoredDRCBuffer.buffering_mode);
}
if (!skipScreen0Free && screenbuffer0) {
MEMFreeToMappedMemory(screenbuffer0);
}
if (!skipScreen1Free && screenbuffer1) {
MEMFreeToMappedMemory(screenbuffer1);
}
}
void ConfigUtils::renderBasicScreen(std::string_view text) {
DrawUtils::beginDraw();
DrawUtils::clear(COLOR_BACKGROUND);
DrawUtils::setFontColor(COLOR_TEXT);
// draw top bar
DrawUtils::setFontSize(24);
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
DrawUtils::setFontSize(18);
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, VERSION_FULL, true);
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
// draw bottom bar
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
DrawUtils::setFontSize(18);
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\ue07d Navigate ");
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\ue000 Select", true);
DrawUtils::setFontSize(24);
uint32_t sz = DrawUtils::getTextWidth(text.data());
DrawUtils::print((SCREEN_WIDTH / 2) - (sz / 2), (SCREEN_HEIGHT / 2), text.data());
// draw home button
DrawUtils::setFontSize(18);
const char *exitHint = "\ue044 Exit";
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
DrawUtils::endDraw();
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "ConfigDefines.h"
#include "ConfigDisplayItem.h"
#include "config/WUPSConfig.h"
#include "utils/input/Input.h"
#include <gx2/enum.h>
#include <memory>
#include <string>
#define MOVE_ITEM_INPUT_MASK (WUPS_CONFIG_BUTTON_B | WUPS_CONFIG_BUTTON_DOWN | WUPS_CONFIG_BUTTON_UP)
class ConfigUtils {
public:
static void openConfigMenu();
static WUPS_CONFIG_SIMPLE_INPUT convertInputs(uint32_t buttons);
private:
static void displayMenu();
static void renderBasicScreen(std::string_view text);
};

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Input.h"
class CombinedInput : public Input {
public:
void combine(const Input &b) {
data.buttons_h |= b.data.buttons_h;
if (!data.touched) {
data.touched = b.data.touched;
}
if (!data.validPointer) {
data.validPointer = b.data.validPointer;
data.pointerAngle = b.data.pointerAngle;
data.x = b.data.x;
data.y = b.data.y;
}
}
void process() {
data.buttons_d |= (data.buttons_h & (~lastData.buttons_h));
data.buttons_r |= (lastData.buttons_h & (~data.buttons_h));
lastData.buttons_h = data.buttons_h;
}
void reset() {
data = {};
}
};

View File

@@ -0,0 +1,62 @@
#pragma once
#include <cstdint>
#include <cstring>
class Input {
public:
//!Constructor
Input() = default;
//!Destructor
virtual ~Input() = default;
enum eButtons {
BUTTON_NONE = 0x0000,
VPAD_TOUCH = 0x80000000,
BUTTON_STICK_L = 0x80000,
BUTTON_STICK_R = 0x40000,
BUTTON_Z = 0x20000,
BUTTON_C = 0x10000,
BUTTON_A = 0x8000,
BUTTON_B = 0x4000,
BUTTON_X = 0x2000,
BUTTON_Y = 0x1000,
BUTTON_1 = BUTTON_Y,
BUTTON_2 = BUTTON_X,
BUTTON_LEFT = 0x0800,
BUTTON_RIGHT = 0x0400,
BUTTON_UP = 0x0200,
BUTTON_DOWN = 0x0100,
BUTTON_ZL = 0x0080,
BUTTON_ZR = 0x0040,
BUTTON_L = 0x0020,
BUTTON_R = 0x0010,
BUTTON_PLUS = 0x0008,
BUTTON_MINUS = 0x0004,
BUTTON_HOME = 0x0002,
BUTTON_SYNC = 0x0001,
STICK_R_LEFT = 0x04000000,
STICK_R_RIGHT = 0x02000000,
STICK_R_UP = 0x01000000,
STICK_R_DOWN = 0x00800000,
STICK_L_LEFT = 0x40000000,
STICK_L_RIGHT = 0x20000000,
STICK_L_UP = 0x10000000,
STICK_L_DOWN = 0x08000000
};
typedef struct {
uint32_t buttons_h;
uint32_t buttons_d;
uint32_t buttons_r;
bool validPointer;
bool touched;
float pointerAngle;
int32_t x;
int32_t y;
} PadData;
PadData data{};
PadData lastData{};
};

View File

@@ -0,0 +1,57 @@
#pragma once
/****************************************************************************
* Copyright (C) 2015 Dimok
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "Input.h"
#include <vpad/input.h>
class VPadInput : public Input {
public:
//!Constructor
VPadInput() = default;
//!Destructor
~VPadInput() override = default;
bool update(int32_t width, int32_t height) {
lastData = data;
data = {};
vpadError = VPAD_READ_NO_SAMPLES;
if (VPADRead(VPAD_CHAN_0, &vpad, 1, &vpadError) > 0 && vpadError == VPAD_READ_SUCCESS) {
data.buttons_r = vpad.release;
data.buttons_h = vpad.hold;
data.buttons_d = vpad.trigger;
data.validPointer = !vpad.tpNormal.validity;
data.touched = vpad.tpNormal.touched;
VPADGetTPCalibratedPoint(VPAD_CHAN_0, &tpCalib, &vpad.tpFiltered1);
//! calculate the screen offsets
data.x = -(width >> 1) + (int32_t) (((float) tpCalib.x / 1280.0f) * (float) width);
data.y = -(height >> 1) + (int32_t) (float) height - (((float) tpCalib.y / 720.0f) * (float) height);
return true;
}
return false;
}
VPADStatus vpad = {};
VPADReadError vpadError = {};
VPADTouchData tpCalib = {};
};

View File

@@ -0,0 +1,187 @@
#pragma once
/****************************************************************************
* Copyright (C) 2015 Dimok
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "Input.h"
#include <padscore/kpad.h>
#include <padscore/wpad.h>
class WPADInput : public Input {
public:
WPADInput(KPADChan channel) {
this->channel = channel;
}
~WPADInput() override {}
uint32_t remapWiiMoteButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_BUTTON_LEFT)
conv_buttons |= Input::BUTTON_LEFT;
if (buttons & WPAD_BUTTON_RIGHT)
conv_buttons |= Input::BUTTON_RIGHT;
if (buttons & WPAD_BUTTON_DOWN)
conv_buttons |= Input::BUTTON_DOWN;
if (buttons & WPAD_BUTTON_UP)
conv_buttons |= Input::BUTTON_UP;
if (buttons & WPAD_BUTTON_PLUS)
conv_buttons |= Input::BUTTON_PLUS;
if (buttons & WPAD_BUTTON_2)
conv_buttons |= Input::BUTTON_2;
if (buttons & WPAD_BUTTON_1)
conv_buttons |= Input::BUTTON_1;
if (buttons & WPAD_BUTTON_B)
conv_buttons |= Input::BUTTON_B;
if (buttons & WPAD_BUTTON_A)
conv_buttons |= Input::BUTTON_A;
if (buttons & WPAD_BUTTON_MINUS)
conv_buttons |= Input::BUTTON_MINUS;
if (buttons & WPAD_BUTTON_Z)
conv_buttons |= Input::BUTTON_Z;
if (buttons & WPAD_BUTTON_C)
conv_buttons |= Input::BUTTON_C;
if (buttons & WPAD_BUTTON_HOME)
conv_buttons |= Input::BUTTON_HOME;
return conv_buttons;
}
uint32_t remapClassicButtons(uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & WPAD_CLASSIC_BUTTON_LEFT)
conv_buttons |= Input::BUTTON_LEFT;
if (buttons & WPAD_CLASSIC_BUTTON_RIGHT)
conv_buttons |= Input::BUTTON_RIGHT;
if (buttons & WPAD_CLASSIC_BUTTON_DOWN)
conv_buttons |= Input::BUTTON_DOWN;
if (buttons & WPAD_CLASSIC_BUTTON_UP)
conv_buttons |= Input::BUTTON_UP;
if (buttons & WPAD_CLASSIC_BUTTON_PLUS)
conv_buttons |= Input::BUTTON_PLUS;
if (buttons & WPAD_CLASSIC_BUTTON_X)
conv_buttons |= Input::BUTTON_X;
if (buttons & WPAD_CLASSIC_BUTTON_Y)
conv_buttons |= Input::BUTTON_Y;
if (buttons & WPAD_CLASSIC_BUTTON_B)
conv_buttons |= Input::BUTTON_B;
if (buttons & WPAD_CLASSIC_BUTTON_A)
conv_buttons |= Input::BUTTON_A;
if (buttons & WPAD_CLASSIC_BUTTON_MINUS)
conv_buttons |= Input::BUTTON_MINUS;
if (buttons & WPAD_CLASSIC_BUTTON_HOME)
conv_buttons |= Input::BUTTON_HOME;
if (buttons & WPAD_CLASSIC_BUTTON_ZR)
conv_buttons |= Input::BUTTON_ZR;
if (buttons & WPAD_CLASSIC_BUTTON_ZL)
conv_buttons |= Input::BUTTON_ZL;
if (buttons & WPAD_CLASSIC_BUTTON_R)
conv_buttons |= Input::BUTTON_R;
if (buttons & WPAD_CLASSIC_BUTTON_L)
conv_buttons |= Input::BUTTON_L;
return conv_buttons;
}
bool update(int32_t width, int32_t height) {
lastData = data;
kpadError = KPAD_ERROR_UNINITIALIZED;
data = {};
WPADExtensionType type;
if (WPADProbe(channel, &type) != 0) {
data.buttons_h = 0;
return false;
}
if (KPADReadEx(channel, &kpad, 1, &kpadError) <= 0) {
return false;
}
if (kpadError != KPAD_ERROR_OK || kpad.extensionType == 0xFF) {
return false;
}
if (kpad.extensionType == WPAD_EXT_CORE || kpad.extensionType == WPAD_EXT_NUNCHUK) {
data.buttons_r = remapWiiMoteButtons(kpad.release);
data.buttons_h = remapWiiMoteButtons(kpad.hold);
data.buttons_d = remapWiiMoteButtons(kpad.trigger);
} else {
data.buttons_r = remapClassicButtons(kpad.classic.release);
data.buttons_h = remapClassicButtons(kpad.classic.hold);
data.buttons_d = remapClassicButtons(kpad.classic.trigger);
}
data.validPointer = (kpad.posValid == 1 || kpad.posValid == 2) &&
(kpad.pos.x >= -1.0f && kpad.pos.x <= 1.0f) &&
(kpad.pos.y >= -1.0f && kpad.pos.y <= 1.0f);
//! calculate the screen offsets if pointer is valid else leave old value
if (data.validPointer) {
data.x = (width >> 1) * kpad.pos.x;
data.y = (height >> 1) * (-kpad.pos.y);
if (kpad.angle.y > 0.0f) {
data.pointerAngle = (-kpad.angle.x + 1.0f) * 0.5f * 180.0f;
} else {
data.pointerAngle = (kpad.angle.x + 1.0f) * 0.5f * 180.0f - 180.0f;
}
}
return true;
}
static void init() {
KPADInit();
WPADEnableURCC(1);
}
static void close() {
}
KPADStatus kpad = {};
KPADError kpadError = KPAD_ERROR_UNINITIALIZED;
KPADChan channel;
};

View File

@@ -55,7 +55,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) while (0)
#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);

View File

@@ -8,6 +8,7 @@
#include <memory>
#include <mutex>
#include <set>
#include <vector>
#ifdef __cplusplus
extern "C" {
@@ -91,12 +92,44 @@ remove_first_if(Container &container, Predicate pred) {
return false;
}
template<typename Container, typename Predicate>
typename std::enable_if<std::is_same<Container, std::vector<typename Container::value_type>>::value, bool>::type
remove_first_if(Container &container, Predicate pred) {
auto it = container.begin();
while (it != container.end()) {
if (pred(*it)) {
container.erase(it);
return true;
}
++it;
}
return false;
}
template<typename Container, typename Predicate>
bool remove_locked_first_if(std::mutex &mutex, Container &container, Predicate pred) {
std::lock_guard<std::mutex> lock(mutex);
return remove_first_if(container, pred);
}
template<typename T, typename Predicate>
T pop_locked_first_if(std::mutex &mutex, std::vector<T> &container, Predicate pred) {
std::lock_guard<std::mutex> lock(mutex);
T result;
auto it = container.begin();
while (it != container.end()) {
if (pred(*it)) {
result = std::move(*it);
container.erase(it);
return result;
}
++it;
}
return result;
}
std::string getPluginPath();
OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr);