Skip to content

Commit

Permalink
RawInput: add 'Modifier' button support, allows per-game input change…
Browse files Browse the repository at this point in the history
…s when held.

By default this will swap any LS movement over to RS instead (eg for games that require RS movement to proceed)
GoldenEye/Perfect Dark use a different modifier that reduces movement speed to 1/2, allowing it to act as a 'walk' button.
  • Loading branch information
emoose committed Feb 18, 2021
1 parent a9486e2 commit 8a829dc
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 97 deletions.
21 changes: 17 additions & 4 deletions bindings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ W = LS-Up
S = LS-Down
A = LS-Left
D = LS-Right
; Default modifier swaps LS movement over to RS
LShift = Modifier
: = A
# = B
L = X
Expand All @@ -15,16 +17,18 @@ E = RT
X = Start
Z = Back
F = LS
Up = RS-Up
Down = RS-Down
Left = RS-Left
Right = RS-Right
Up = Up
Down = Down
Left = Left
Right = Right

[584108A9 - GoldenEye XBLA]
W = LS-Up
S = LS-Down
A = LS-Left
D = LS-Right
; GoldenEye modifier reduces LS movement to 1/2 (ie allows for a 'walk' button)
LShift = Modifier
E = A
R = X
Q = Y
Expand All @@ -48,6 +52,7 @@ W = LS-Up
S = LS-Down
A = LS-Left
D = LS-Right
; Perfect Dark modifier reduces LS movement to 1/2 (ie allows for a 'walk' button)
E = A
R = X
Q = LB
Expand Down Expand Up @@ -77,6 +82,8 @@ W = LS-Up
S = LS-Down
A = LS-Left
D = LS-Right
; Halo uses default modifier, swaps LS movement over to RS
LShift = Modifier
E = A
Space = A
C = LS
Expand Down Expand Up @@ -105,6 +112,8 @@ W = LS-Up
S = LS-Down
A = LS-Left
D = LS-Right
; Halo uses default modifier, swaps LS movement over to RS
LShift = Modifier
E = A
Space = A
C = LS
Expand Down Expand Up @@ -133,6 +142,8 @@ W = LS-Up
S = LS-Down
A = LS-Left
D = LS-Right
; Halo uses default modifier, swaps LS movement over to RS
LShift = Modifier
E = A
Space = A
C = LS
Expand Down Expand Up @@ -161,6 +172,8 @@ W = LS-Up
S = LS-Down
A = LS-Left
D = LS-Right
; Halo uses default modifier, swaps LS movement over to RS
LShift = Modifier
E = A
Space = A
C = LS
Expand Down
23 changes: 23 additions & 0 deletions src/xenia/hid/winkey/hookables/goldeneye.cc
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,29 @@ bool GoldeneyeGame::DoHooks(uint32_t user_index, RawInputState& input_state,
return true;
}

// GE modifier reduces LS-movement, to allow for walk speed to be reduced
// (ie a 'walk' button)
bool GoldeneyeGame::ModifierKeyHandler(uint32_t user_index,
RawInputState& input_state,
X_INPUT_STATE* out_state) {

float thumb_lx = (int16_t)out_state->gamepad.thumb_lx;
float thumb_ly = (int16_t)out_state->gamepad.thumb_ly;

// Work out angle from the current stick values
float angle = atan2f(thumb_ly, thumb_lx);

// Sticks get set to SHRT_MAX if key pressed, use half of that
float distance = (float)SHRT_MAX;
distance /= 2;

out_state->gamepad.thumb_lx = (int16_t)(distance * cosf(angle));
out_state->gamepad.thumb_ly = (int16_t)(distance * sinf(angle));

// Return true to signal that we've handled the modifier, so default modifier won't be used
return true;
}

} // namespace winkey
} // namespace hid
} // namespace xe
2 changes: 2 additions & 0 deletions src/xenia/hid/winkey/hookables/goldeneye.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class GoldeneyeGame : public HookableGame {
bool IsGameSupported();
bool DoHooks(uint32_t user_index, RawInputState& input_state,
X_INPUT_STATE* out_state);
bool ModifierKeyHandler(uint32_t user_index, RawInputState& input_state,
X_INPUT_STATE* out_state);

private:
GameBuild game_build_ = GameBuild::Unknown;
Expand Down
6 changes: 6 additions & 0 deletions src/xenia/hid/winkey/hookables/halo3.cc
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ bool Halo3Game::DoHooks(uint32_t user_index, RawInputState& input_state,
return true;
}

bool Halo3Game::ModifierKeyHandler(uint32_t user_index,
RawInputState& input_state,
X_INPUT_STATE* out_state) {
// Defer to default modifier (swaps LS movement over to RS)
return false;
}

} // namespace winkey
} // namespace hid
Expand Down
2 changes: 2 additions & 0 deletions src/xenia/hid/winkey/hookables/halo3.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class Halo3Game : public HookableGame {
bool IsGameSupported();
bool DoHooks(uint32_t user_index, RawInputState& input_state,
X_INPUT_STATE* out_state);
bool ModifierKeyHandler(uint32_t user_index, RawInputState& input_state,
X_INPUT_STATE* out_state);

private:
GameBuild game_build_ = GameBuild::Unknown;
Expand Down
3 changes: 3 additions & 0 deletions src/xenia/hid/winkey/hookables/hookable_game.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class HookableGame {
virtual bool IsGameSupported() = 0;
virtual bool DoHooks(uint32_t user_index, RawInputState& input_state,
X_INPUT_STATE* out_state) = 0;
virtual bool ModifierKeyHandler(uint32_t user_index,
RawInputState& input_state,
X_INPUT_STATE* out_state) = 0;
};

} // namespace winkey
Expand Down
162 changes: 81 additions & 81 deletions src/xenia/hid/winkey/winkey_input_driver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ static const std::unordered_map<std::string, uint32_t> kXInputButtons = {
{"rs-down", XINPUT_BIND_RS_DOWN},
{"rs-left", XINPUT_BIND_RS_LEFT},
{"rs-right", XINPUT_BIND_RS_RIGHT},

{"modifier", XINPUT_BIND_MODIFIER}
};

static const std::unordered_map<std::string, uint32_t> kKeyMap = {
Expand Down Expand Up @@ -93,6 +95,7 @@ static const std::unordered_map<std::string, uint32_t> kKeyMap = {

{"lshift", VK_LSHIFT},
{"shift", VK_LSHIFT},
{"rshift", VK_RSHIFT},

{"backspace", VK_BACK},
{"down", VK_DOWN},
Expand Down Expand Up @@ -434,13 +437,13 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
int16_t thumb_ly = 0;
int16_t thumb_rx = 0;
int16_t thumb_ry = 0;
bool modifier_pressed = false;

X_RESULT result = X_ERROR_SUCCESS;

RawInputState state;

if (window()->has_focus() && is_active()) {

{
std::unique_lock<std::mutex> mouse_lock(mouse_mutex_);
while (!mouse_events_.empty()) {
Expand All @@ -457,93 +460,77 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
state.mouse.wheel_delta = -state.mouse.wheel_delta;
}
}

{
std::unique_lock<std::mutex> key_lock(key_mutex_);
state.key_states = key_states_;

// Handle key bindings
if (IS_KEY_DOWN(VK_RSHIFT)) {
// RS toggled
if (IS_KEY_DOWN('A')) {
// A
thumb_rx = SHRT_MIN;
}
if (IS_KEY_DOWN('D')) {
// D
thumb_rx = SHRT_MAX;
}
if (IS_KEY_DOWN('S')) {
// S
thumb_ry = SHRT_MIN;
}
if (IS_KEY_DOWN('W')) {
// W
thumb_ry = SHRT_MAX;
}
} else {
uint32_t cur_game = xe::kernel::kernel_state()->title_id();
if (!key_binds_.count(cur_game)) {
cur_game = kTitleIdDefaultBindings;
}
if (key_binds_.count(cur_game)) {
auto& binds = key_binds_.at(cur_game);
auto process_binding = [binds, &buttons, &left_trigger, &right_trigger,
&thumb_lx, &thumb_ly, &thumb_rx,
&thumb_ry](uint32_t key) {
if (!binds.count(key)) {
return;
}
auto binding = binds.at(key);
buttons |= (binding & XINPUT_BUTTONS_MASK);

if (binding & XINPUT_BIND_LEFT_TRIGGER) {
left_trigger = 0xFF;
}

if (binding & XINPUT_BIND_RIGHT_TRIGGER) {
right_trigger = 0xFF;
}

if (binding & XINPUT_BIND_LS_UP) {
thumb_ly = SHRT_MAX;
}
if (binding & XINPUT_BIND_LS_DOWN) {
thumb_ly = SHRT_MIN;
}
if (binding & XINPUT_BIND_LS_LEFT) {
thumb_lx = SHRT_MIN;
}
if (binding & XINPUT_BIND_LS_RIGHT) {
thumb_lx = SHRT_MAX;
}

if (binding & XINPUT_BIND_RS_UP) {
thumb_ry = SHRT_MAX;
}
if (binding & XINPUT_BIND_RS_DOWN) {
thumb_ry = SHRT_MIN;
}
if (binding & XINPUT_BIND_RS_LEFT) {
thumb_rx = SHRT_MIN;
}
if (binding & XINPUT_BIND_RS_RIGHT) {
thumb_rx = SHRT_MAX;
}
};

if (state.mouse.wheel_delta != 0) {
if (state.mouse.wheel_delta > 0) {
process_binding(VK_BIND_MWHEELUP);
} else {
process_binding(VK_BIND_MWHEELDOWN);
}
uint32_t cur_game = xe::kernel::kernel_state()->title_id();
if (!key_binds_.count(cur_game)) {
cur_game = kTitleIdDefaultBindings;
}
if (key_binds_.count(cur_game)) {
auto& binds = key_binds_.at(cur_game);
auto process_binding = [binds, &buttons, &left_trigger, &right_trigger,
&thumb_lx, &thumb_ly, &thumb_rx,
&thumb_ry, &modifier_pressed](uint32_t key) {
if (!binds.count(key)) {
return;
}
auto binding = binds.at(key);
buttons |= (binding & XINPUT_BUTTONS_MASK);

if (binding & XINPUT_BIND_LEFT_TRIGGER) {
left_trigger = 0xFF;
}

if (binding & XINPUT_BIND_RIGHT_TRIGGER) {
right_trigger = 0xFF;
}

if (binding & XINPUT_BIND_LS_UP) {
thumb_ly = SHRT_MAX;
}
if (binding & XINPUT_BIND_LS_DOWN) {
thumb_ly = SHRT_MIN;
}
if (binding & XINPUT_BIND_LS_LEFT) {
thumb_lx = SHRT_MIN;
}
if (binding & XINPUT_BIND_LS_RIGHT) {
thumb_lx = SHRT_MAX;
}

if (binding & XINPUT_BIND_RS_UP) {
thumb_ry = SHRT_MAX;
}
if (binding & XINPUT_BIND_RS_DOWN) {
thumb_ry = SHRT_MIN;
}
if (binding & XINPUT_BIND_RS_LEFT) {
thumb_rx = SHRT_MIN;
}
if (binding & XINPUT_BIND_RS_RIGHT) {
thumb_rx = SHRT_MAX;
}

for (int i = 0; i < 0x100; i++) {
if (key_states_[i]) {
process_binding(i);
}
if (binding & XINPUT_BIND_MODIFIER) {
modifier_pressed = true;
}
};

if (state.mouse.wheel_delta != 0) {
if (state.mouse.wheel_delta > 0) {
process_binding(VK_BIND_MWHEELUP);
} else {
process_binding(VK_BIND_MWHEELDOWN);
}
}

for (int i = 0; i < 0x100; i++) {
if (key_states_[i]) {
process_binding(i);
}
}
}
Expand All @@ -560,14 +547,27 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
out_state->gamepad.thumb_ry = thumb_ry;

// Check if we have any hooks/injections for the current game
bool game_modifier_handled = false;
for (auto& game : hookable_games_) {
if (game->IsGameSupported()) {
std::unique_lock<std::mutex> key_lock(key_mutex_);
game->DoHooks(user_index, state, out_state);
if (modifier_pressed) {
game_modifier_handled =
game->ModifierKeyHandler(user_index, state, out_state);
}
break;
}
}

if (!game_modifier_handled && modifier_pressed) {
// Modifier not handled by any supported game class, apply default modifier
// (swap LS input to RS, for games that require RS movement)
out_state->gamepad.thumb_rx = out_state->gamepad.thumb_lx;
out_state->gamepad.thumb_ry = out_state->gamepad.thumb_ly;
out_state->gamepad.thumb_lx = out_state->gamepad.thumb_ly = 0;
}

return result;
}

Expand Down
26 changes: 14 additions & 12 deletions src/xenia/hid/winkey/winkey_input_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@
#include "xenia/hid/winkey/hookables/hookable_game.h"

#define XINPUT_BUTTONS_MASK 0xFFFF
#define XINPUT_BIND_LEFT_TRIGGER 0x10000
#define XINPUT_BIND_RIGHT_TRIGGER 0x20000

#define XINPUT_BIND_LS_UP 0x100000
#define XINPUT_BIND_LS_DOWN 0x200000
#define XINPUT_BIND_LS_LEFT 0x400000
#define XINPUT_BIND_LS_RIGHT 0x800000

#define XINPUT_BIND_RS_UP 0x1000000
#define XINPUT_BIND_RS_DOWN 0x2000000
#define XINPUT_BIND_RS_LEFT 0x4000000
#define XINPUT_BIND_RS_RIGHT 0x8000000
#define XINPUT_BIND_LEFT_TRIGGER (1 << 16)
#define XINPUT_BIND_RIGHT_TRIGGER (1 << 17)

#define XINPUT_BIND_LS_UP (1 << 18)
#define XINPUT_BIND_LS_DOWN (1 << 19)
#define XINPUT_BIND_LS_LEFT (1 << 20)
#define XINPUT_BIND_LS_RIGHT (1 << 21)

#define XINPUT_BIND_RS_UP (1 << 22)
#define XINPUT_BIND_RS_DOWN (1 << 23)
#define XINPUT_BIND_RS_LEFT (1 << 24)
#define XINPUT_BIND_RS_RIGHT (1 << 25)

#define XINPUT_BIND_MODIFIER (1 << 26)

#define VK_BIND_MWHEELUP 0x10000
#define VK_BIND_MWHEELDOWN 0x20000
Expand Down

0 comments on commit 8a829dc

Please sign in to comment.