mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-23 06:26:12 -04:00
169 lines
4.8 KiB
C++
169 lines
4.8 KiB
C++
#include "friend_code_keyboard.h"
|
|
|
|
#include "abi_bridge.h"
|
|
#include "guest_abi/guest_region.h"
|
|
#include "guest_function_imports.h"
|
|
#include "input_bindings.h"
|
|
#include "memory.h"
|
|
#include "runtime_product.h"
|
|
|
|
#include <SDL3/SDL_clipboard.h>
|
|
#include <SDL3/SDL_events.h>
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
// UI::RegisterFriendPage
|
|
constexpr uint32_t kDigitButtonsOffset = 0x44;
|
|
constexpr uint32_t kPushButtonSize = 0x254;
|
|
constexpr uint32_t kDigitButtonCount = 10;
|
|
constexpr uint32_t kButtonDigitOffset = 0x240;
|
|
constexpr uint32_t kPageStateOffset = 0x8;
|
|
constexpr uint32_t kPageStateActive = 4;
|
|
constexpr uint32_t kVtableGetTypeInfoOffset = 0x60;
|
|
constexpr int kBackspace = -1;
|
|
constexpr size_t kMaxPendingKeys = 32;
|
|
constexpr size_t kFriendCodeDigits = 12;
|
|
|
|
std::mutex g_pendingMutex;
|
|
std::vector<int> g_pending;
|
|
std::atomic_bool g_pageFocused{false};
|
|
|
|
bool IsPaste(const SDL_KeyboardEvent& key) {
|
|
return !key.repeat && key.key == SDLK_V && (key.mod & (SDL_KMOD_CTRL | SDL_KMOD_GUI)) != 0 &&
|
|
(key.mod & SDL_KMOD_ALT) == 0;
|
|
}
|
|
|
|
void AppendClipboardDigits(std::vector<int>& keys) {
|
|
char* text = SDL_GetClipboardText();
|
|
if (!text) {
|
|
return;
|
|
}
|
|
size_t digits = 0;
|
|
for (const char* c = text; *c && digits < kFriendCodeDigits; ++c) {
|
|
if (*c >= '0' && *c <= '9') {
|
|
keys.push_back(*c - '0');
|
|
++digits;
|
|
}
|
|
}
|
|
SDL_free(text);
|
|
}
|
|
|
|
std::optional<int> KeyValue(const SDL_KeyboardEvent& key) {
|
|
if ((key.mod & (SDL_KMOD_CTRL | SDL_KMOD_ALT | SDL_KMOD_GUI)) != 0) {
|
|
return std::nullopt;
|
|
}
|
|
if (key.scancode == SDL_SCANCODE_BACKSPACE) {
|
|
return kBackspace;
|
|
}
|
|
if (key.repeat) {
|
|
return std::nullopt;
|
|
}
|
|
if (key.scancode == SDL_SCANCODE_0 || key.scancode == SDL_SCANCODE_KP_0) {
|
|
return 0;
|
|
}
|
|
if (key.scancode >= SDL_SCANCODE_1 && key.scancode <= SDL_SCANCODE_9) {
|
|
return 1 + (key.scancode - SDL_SCANCODE_1);
|
|
}
|
|
if (key.scancode >= SDL_SCANCODE_KP_1 && key.scancode <= SDL_SCANCODE_KP_9) {
|
|
return 1 + (key.scancode - SDL_SCANCODE_KP_1);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
bool IsFocusedRegisterFriendPage(uint32_t page) {
|
|
try {
|
|
const uint32_t vtable = Memory::Read32(page);
|
|
return Memory::Read32(vtable + kVtableGetTypeInfoOffset) == MKW_GADDR(8061E804) &&
|
|
Memory::Read32(page + kPageStateOffset) == kPageStateActive;
|
|
} catch (const Memory::AccessViolation&) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::optional<uint32_t> DigitButton(uint32_t page, int digit) {
|
|
for (uint32_t i = 0; i < kDigitButtonCount; ++i) {
|
|
const uint32_t button = page + kDigitButtonsOffset + i * kPushButtonSize;
|
|
if (static_cast<int>(Memory::Read32(button + kButtonDigitOffset)) == digit) {
|
|
return button;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
void Type(CpuContext* ctx, uint32_t page, int key) {
|
|
if (key == kBackspace) {
|
|
ctx->gpr[3] = page;
|
|
MKW_GUEST_FUNC(8061E3B4)(ctx);
|
|
return;
|
|
}
|
|
const std::optional<uint32_t> button = DigitButton(page, key);
|
|
if (!button) {
|
|
return;
|
|
}
|
|
ctx->gpr[3] = page;
|
|
ctx->gpr[4] = *button;
|
|
ctx->gpr[5] = 0;
|
|
MKW_GUEST_FUNC(8061E348)(ctx);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace FriendCodeKeyboard {
|
|
|
|
void HandleEvents(const AuroraEvent* events) noexcept {
|
|
if (!events || !g_pageFocused.load(std::memory_order_acquire) || InputBindings::InputBlocked()) {
|
|
return;
|
|
}
|
|
std::vector<int> keys;
|
|
for (const AuroraEvent* ev = events; ev->type != AURORA_NONE; ++ev) {
|
|
if (ev->type != AURORA_SDL_EVENT || ev->sdl.type != SDL_EVENT_KEY_DOWN) {
|
|
continue;
|
|
}
|
|
if (IsPaste(ev->sdl.key)) {
|
|
AppendClipboardDigits(keys);
|
|
} else if (const std::optional<int> key = KeyValue(ev->sdl.key)) {
|
|
keys.push_back(*key);
|
|
}
|
|
}
|
|
if (keys.empty()) {
|
|
return;
|
|
}
|
|
std::lock_guard lock(g_pendingMutex);
|
|
for (const int key : keys) {
|
|
if (g_pending.size() >= kMaxPendingKeys) {
|
|
break;
|
|
}
|
|
g_pending.push_back(key);
|
|
}
|
|
}
|
|
|
|
} // namespace FriendCodeKeyboard
|
|
|
|
// Page::CheckActions, only ever called for the topmost page.
|
|
extern "C" void Page_CheckActions_80601c64(CpuContext* ctx) {
|
|
const uint32_t page = ctx->gpr[3];
|
|
MKW_GUEST_FUNC(80601C64)(ctx);
|
|
|
|
const bool focused = RuntimeProduct::IsRetroRewind() && IsFocusedRegisterFriendPage(page);
|
|
g_pageFocused.store(focused, std::memory_order_release);
|
|
std::vector<int> keys;
|
|
{
|
|
std::lock_guard lock(g_pendingMutex);
|
|
keys.swap(g_pending);
|
|
}
|
|
if (!focused || InputBindings::InputBlocked()) {
|
|
return;
|
|
}
|
|
for (const int key : keys) {
|
|
Type(ctx, page, key);
|
|
}
|
|
}
|
|
|
|
REGISTER_NATIVE_FUNCTION_AS(0x80601C64, Page_CheckActions_80601c64, "Page_CheckActions_80601c64");
|