mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 18:01:03 +00:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
184 lines
8.2 KiB
C
184 lines
8.2 KiB
C
// Binding tables (keyboard/mouse and gamepad), their totk.ini overrides and the pure mapping functions (input_map.h).
|
|
// No Win32 or SDL here.
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "cfg.h"
|
|
#include "host.h"
|
|
#include "input_map.h"
|
|
|
|
#define STICK_MAX 32767
|
|
#define DIAGONAL 23170 // STICK_MAX / sqrt(2)
|
|
#define MOUSE_FULL_CPS 3000.0
|
|
|
|
#define DEFAULT_KEYS \
|
|
{KEY_W, B_SL_UP}, {KEY_A, B_SL_LEFT}, {KEY_S, B_SL_DOWN}, {KEY_D, B_SL_RIGHT}, /* move */ \
|
|
{KEY_UP, B_SR_UP}, {KEY_LEFT, B_SR_LEFT}, {KEY_DOWN, B_SR_DOWN}, {KEY_RIGHT, B_SR_RIGHT}, /* camera, second source */ \
|
|
{KEY_MOUSE_L, B_Y}, {KEY_MOUSE_R, B_ZL}, {KEY_Z, B_ZL}, {KEY_F, B_ZR}, {KEY_X, B_ZR}, \
|
|
{KEY_SPACE, B_X}, {KEY_LSHIFT, B_B}, {KEY_E, B_A}, {KEY_Q, B_L}, {KEY_R, B_R}, \
|
|
{KEY_LCTRL, B_STICK_L}, {KEY_C, B_STICK_R}, \
|
|
{KEY_1, B_LEFT}, {KEY_2, B_UP}, {KEY_3, B_RIGHT}, {KEY_4, B_DOWN}, \
|
|
{KEY_TAB, B_PLUS}, {KEY_M, B_MINUS}
|
|
|
|
// By position: south is Switch B, east A, west Y, north X (Nintendo layout); the triggers are ZL and ZR.
|
|
#define DEFAULT_PADS \
|
|
{PAD_SOUTH, B_B}, {PAD_EAST, B_A}, {PAD_WEST, B_Y}, {PAD_NORTH, B_X}, {PAD_LSTICK, B_STICK_L}, {PAD_RSTICK, B_STICK_R}, \
|
|
{PAD_L, B_L}, {PAD_R, B_R}, {PAD_START, B_PLUS}, {PAD_BACK, B_MINUS}, {PAD_DPAD_LEFT, B_LEFT}, {PAD_DPAD_UP, B_UP}, \
|
|
{PAD_DPAD_RIGHT, B_RIGHT}, {PAD_DPAD_DOWN, B_DOWN}, {PAD_ZL, B_ZL}, {PAD_ZR, B_ZR}
|
|
|
|
static const Binding default_keys[] = {DEFAULT_KEYS};
|
|
static const PadBinding default_pads[] = {DEFAULT_PADS};
|
|
Binding input_bindings[INPUT_BINDING_MAX] = {DEFAULT_KEYS};
|
|
int input_binding_count = (int)(sizeof default_keys / sizeof *default_keys);
|
|
PadBinding input_pad_bindings[INPUT_PAD_BINDING_MAX] = {DEFAULT_PADS};
|
|
int input_pad_binding_count = (int)(sizeof default_pads / sizeof *default_pads);
|
|
|
|
static const char *const button_names[16] = {"A", "B", "X", "Y", "LCLICK", "RCLICK", "L", "R", "ZL", "ZR", "PLUS", "MINUS",
|
|
"LEFT", "UP", "RIGHT", "DOWN"}; // indexed by action
|
|
static const char *const pad_names[PAD_COUNT] = {
|
|
NULL, "south", "east", "west", "north", "lstick", "rstick", "l", "r", "start", "back",
|
|
"dpad_left", "dpad_up", "dpad_right", "dpad_down", "zl", "zr"};
|
|
static const char *const key_names[KEY_COUNT] = { // letters and digits are handled in key_id
|
|
[KEY_SPACE] = "space", [KEY_TAB] = "tab", [KEY_LSHIFT] = "lshift", [KEY_LCTRL] = "lctrl", [KEY_UP] = "up",
|
|
[KEY_DOWN] = "down", [KEY_LEFT] = "left", [KEY_RIGHT] = "right", [KEY_MOUSE_L] = "mouse_l", [KEY_MOUSE_R] = "mouse_r",
|
|
[KEY_MOUSE_M] = "mouse_m"};
|
|
|
|
void input_map_reset(void) {
|
|
memcpy(input_bindings, default_keys, sizeof default_keys);
|
|
input_binding_count = (int)(sizeof default_keys / sizeof *default_keys);
|
|
memcpy(input_pad_bindings, default_pads, sizeof default_pads);
|
|
input_pad_binding_count = (int)(sizeof default_pads / sizeof *default_pads);
|
|
}
|
|
|
|
static int key_id(const char *name) {
|
|
if (name[0] && !name[1]) {
|
|
int c = tolower((unsigned char)name[0]);
|
|
if (c >= 'a' && c <= 'z') return KEY_A + (c - 'a');
|
|
if (c >= '0' && c <= '9') return KEY_0 + (c - '0');
|
|
}
|
|
for (int k = KEY_SPACE; k < KEY_COUNT; k++)
|
|
if (key_names[k] && !_stricmp(key_names[k], name)) return k;
|
|
return KEY_NONE;
|
|
}
|
|
|
|
static int pad_id(const char *name) {
|
|
for (int p = PAD_SOUTH; p < PAD_COUNT; p++)
|
|
if (!_stricmp(pad_names[p], name)) return p;
|
|
return PAD_NONE;
|
|
}
|
|
|
|
static char *trim(char *s) {
|
|
while (*s == ' ' || *s == '\t') s++;
|
|
char *e = s + strlen(s);
|
|
while (e > s && (e[-1] == ' ' || e[-1] == '\t' || e[-1] == '\r')) *--e = 0;
|
|
return s;
|
|
}
|
|
|
|
int input_map_bind(const char *button, const char *list) {
|
|
int action = -1;
|
|
for (int a = 0; a < 16; a++)
|
|
if (!_stricmp(button_names[a], button)) action = a;
|
|
if (action < 0) { rt_log("input: bind: unknown button '%s'", button); return 0; }
|
|
Binding keys[INPUT_BINDING_MAX];
|
|
PadBinding pads[INPUT_PAD_BINDING_MAX];
|
|
int nk = 0, np = 0, none = 0, bad = 0;
|
|
char buf[256];
|
|
snprintf(buf, sizeof buf, "%s", list);
|
|
for (char *save = NULL, *tok = strtok_s(buf, ",", &save); tok; tok = strtok_s(NULL, ",", &save)) {
|
|
tok = trim(tok);
|
|
if (!_stricmp(tok, "none")) { none = 1; continue; }
|
|
int ok = 0;
|
|
if (!_strnicmp(tok, "pad:", 4)) {
|
|
int p = pad_id(trim(tok + 4));
|
|
if (p && np < INPUT_PAD_BINDING_MAX) pads[np++] = (PadBinding){(uint8_t)p, (uint8_t)action}, ok = 1;
|
|
} else if (!_strnicmp(tok, "key:", 4)) {
|
|
int k = key_id(trim(tok + 4));
|
|
if (k && nk < INPUT_BINDING_MAX) keys[nk++] = (Binding){(uint8_t)k, (uint8_t)action}, ok = 1;
|
|
}
|
|
if (!ok) { bad++; rt_log("input: bind %s: ignoring '%s' (expected pad:<name> or key:<name>)", button, tok); }
|
|
}
|
|
if (!nk && !np && !none) { if (bad) rt_log("input: bind %s: no valid entry, keeping the defaults", button); return 0; }
|
|
int w = 0;
|
|
for (int i = 0; i < input_binding_count; i++)
|
|
if (input_bindings[i].action != action) input_bindings[w++] = input_bindings[i];
|
|
input_binding_count = w;
|
|
for (int i = 0; i < nk && input_binding_count < INPUT_BINDING_MAX; i++) input_bindings[input_binding_count++] = keys[i];
|
|
w = 0;
|
|
for (int i = 0; i < input_pad_binding_count; i++)
|
|
if (input_pad_bindings[i].action != action) input_pad_bindings[w++] = input_pad_bindings[i];
|
|
input_pad_binding_count = w;
|
|
for (int i = 0; i < np && input_pad_binding_count < INPUT_PAD_BINDING_MAX; i++)
|
|
input_pad_bindings[input_pad_binding_count++] = pads[i];
|
|
return 1;
|
|
}
|
|
|
|
void input_map_configure(void) {
|
|
input_map_reset();
|
|
for (int a = 0; a < 16; a++) {
|
|
char var[32];
|
|
snprintf(var, sizeof var, "TOTK_BIND_%s", button_names[a]);
|
|
const char *v = cfg_get(var);
|
|
if (v && input_map_bind(button_names[a], v)) rt_log("input: %s = %s", var + 5, v);
|
|
}
|
|
}
|
|
|
|
uint64_t input_map_pad_buttons(const uint8_t down[PAD_COUNT]) {
|
|
uint64_t b = 0;
|
|
for (int i = 0; i < input_pad_binding_count; i++)
|
|
if (down[input_pad_bindings[i].pad]) b |= 1ull << input_pad_bindings[i].action;
|
|
return b;
|
|
}
|
|
|
|
uint64_t input_map_buttons(const uint8_t down[KEY_COUNT]) {
|
|
uint64_t b = 0;
|
|
for (int i = 0; i < input_binding_count; i++)
|
|
if (input_bindings[i].action < B_SL_LEFT && down[input_bindings[i].key]) b |= 1ull << input_bindings[i].action;
|
|
return b;
|
|
}
|
|
|
|
void input_map_stick(const uint8_t down[KEY_COUNT], int stick, int32_t out[2]) {
|
|
int first = stick ? B_SR_LEFT : B_SL_LEFT, dir[4] = {0, 0, 0, 0}; // left, up, right, down
|
|
for (int i = 0; i < input_binding_count; i++) {
|
|
int a = input_bindings[i].action - first;
|
|
if (a >= 0 && a < 4 && down[input_bindings[i].key]) dir[a] = 1;
|
|
}
|
|
int dx = dir[2] - dir[0], dy = dir[1] - dir[3];
|
|
int32_t m = dx && dy ? DIAGONAL : STICK_MAX;
|
|
out[0] = dx * m;
|
|
out[1] = dy * m;
|
|
}
|
|
|
|
void input_mouse_stick(int32_t dx, int32_t dy, double dt_s, float sens, int32_t out[2]) {
|
|
out[0] = out[1] = 0;
|
|
if (!(dt_s > 0) || !(sens > 0)) return;
|
|
double k = STICK_MAX * (double)sens / (MOUSE_FULL_CPS * dt_s);
|
|
double x = dx * k, y = -dy * k, len = sqrt(x * x + y * y);
|
|
if (len > STICK_MAX) { x *= STICK_MAX / len; y *= STICK_MAX / len; }
|
|
out[0] = (int32_t)lrint(x);
|
|
out[1] = (int32_t)lrint(y);
|
|
}
|
|
|
|
// Minus and Plus held together open or close the menu. *held: latched from the poll where both go down until neither is
|
|
// down (the caller keeps it). Every poll of the latch reports neither to the game, so releasing one button first does not
|
|
// leak the other; Minus alone before Plus reaches the game unchanged. With the menu open the game sees a neutral pad.
|
|
// Returns 1 in the poll where both go down: the caller toggles the menu once.
|
|
int input_menu_gate(int *held, int menu_open, uint64_t *buttons, int32_t sticks[4]) {
|
|
const uint64_t combo = 1ull << B_MINUS | 1ull << B_PLUS;
|
|
uint64_t raw = *buttons & combo;
|
|
int edge = 0;
|
|
if (*held) {
|
|
*held = raw != 0;
|
|
} else if (raw == combo) {
|
|
*held = 1;
|
|
edge = 1;
|
|
}
|
|
if (*held) *buttons &= ~combo;
|
|
if (menu_open) {
|
|
*buttons = 0;
|
|
sticks[0] = sticks[1] = sticks[2] = sticks[3] = 0;
|
|
}
|
|
return edge;
|
|
}
|