jak-project/game/system/hid/input_bindings.cpp
water111 4f537d4a71
[jak3] Set up ckernel (#3308)
This sets up the C Kernel for Jak 3, and makes it possible to build and
load code built with `goalc --jak3`.

There's not too much interesting here, other than they switched to a
system where symbol IDs (unique numbers less than 2^14) are generated at
compile time, and those get included in the object file itself.

This is kind of annoying, since it means all tools that produce a GOAL
object file need to work together to assign unique symbol IDs. And since
the symbol IDs can't conflict, and are only a number between 0 and 2^14,
you can't just hash and hope for no collisions.

We work around this by ignoring the IDs and re-assigning our own. I
think this is very similar to what the C Kernel did on early builds of
Jak 3 which supported loading old format level files, which didn't have
the IDs included.

As far as I can tell, this shouldn't cause any problems. It defeats all
of their fancy tricks to save memory by not storing the symbol string,
but we don't care.
2024-01-16 19:24:02 -05:00

398 lines
16 KiB
C++

#include "input_bindings.h"
#include <algorithm>
#include "common/util/json_util.h"
#include "game/system/hid/sdl_util.h"
#include "third-party/SDL/include/SDL.h"
InputModifiers::InputModifiers(const u16 sdl_mod_state) {
need_shift = sdl_mod_state & KMOD_SHIFT;
need_alt = sdl_mod_state & KMOD_ALT;
need_ctrl = sdl_mod_state & KMOD_CTRL;
need_meta = sdl_mod_state & KMOD_GUI;
}
bool InputModifiers::has_necessary_modifiers(const u16 key_modifiers) const {
if (need_alt && ((key_modifiers & KMOD_ALT) == 0)) {
return false;
}
if (need_ctrl && ((key_modifiers & KMOD_CTRL) == 0)) {
return false;
}
if (need_meta && ((key_modifiers & KMOD_GUI) == 0)) {
return false;
}
if (need_shift && ((key_modifiers & KMOD_SHIFT) == 0)) {
return false;
}
return true;
}
void to_json(json& j, const InputModifiers& obj) {
j = json{{"need_shift", obj.need_shift},
{"need_ctrl", obj.need_ctrl},
{"need_meta", obj.need_meta},
{"need_alt", obj.need_alt}};
}
void from_json(const json& j, InputModifiers& obj) {
json_deserialize_if_exists(need_shift);
json_deserialize_if_exists(need_ctrl);
json_deserialize_if_exists(need_meta);
json_deserialize_if_exists(need_alt);
}
void to_json(json& j, const InputBinding& obj) {
j = json{{"pad_data_index", obj.pad_data_index},
{"minimum_in_range", obj.minimum_in_range},
{"modifiers", obj.modifiers}};
}
void from_json(const json& j, InputBinding& obj) {
json_deserialize_if_exists(pad_data_index);
json_deserialize_if_exists(minimum_in_range);
json_deserialize_if_exists(modifiers);
}
void to_json(json& j, const InputBindingGroups& obj) {
j = json{{"device_type", obj.device_type},
{"analog_axii", obj.analog_axii},
{"button_axii", obj.button_axii},
{"buttons", obj.buttons}};
}
void from_json(const json& j, InputBindingGroups& obj) {
json_deserialize_if_exists(device_type);
json_deserialize_if_exists(analog_axii);
json_deserialize_if_exists(button_axii);
json_deserialize_if_exists(buttons);
}
const std::vector<PadData::ButtonIndex> PAD_DATA_PRESSURE_INDEX_ORDER = {
PadData::ButtonIndex::DPAD_RIGHT, PadData::ButtonIndex::DPAD_LEFT,
PadData::ButtonIndex::DPAD_UP, PadData::ButtonIndex::DPAD_DOWN,
PadData::ButtonIndex::TRIANGLE, PadData::ButtonIndex::CIRCLE,
PadData::ButtonIndex::CROSS, PadData::ButtonIndex::SQUARE,
PadData::ButtonIndex::L1, PadData::ButtonIndex::R1,
PadData::ButtonIndex::L2, PadData::ButtonIndex::R2};
const InputBindingGroups DEFAULT_CONTROLLER_BINDS = InputBindingGroups(
CONTROLLER,
{{SDL_CONTROLLER_AXIS_LEFTX, {InputBinding(PadData::AnalogIndex::LEFT_X)}},
{SDL_CONTROLLER_AXIS_LEFTY, {InputBinding(PadData::AnalogIndex::LEFT_Y)}},
{SDL_CONTROLLER_AXIS_RIGHTX, {InputBinding(PadData::AnalogIndex::RIGHT_X)}},
{SDL_CONTROLLER_AXIS_RIGHTY, {InputBinding(PadData::AnalogIndex::RIGHT_Y)}}},
{
{SDL_CONTROLLER_AXIS_TRIGGERLEFT, {InputBinding(PadData::ButtonIndex::L2)}},
{SDL_CONTROLLER_AXIS_TRIGGERRIGHT, {InputBinding(PadData::ButtonIndex::R2)}},
},
{{SDL_CONTROLLER_BUTTON_A, {InputBinding(PadData::ButtonIndex::CROSS)}},
{SDL_CONTROLLER_BUTTON_B, {InputBinding(PadData::ButtonIndex::CIRCLE)}},
{SDL_CONTROLLER_BUTTON_X, {InputBinding(PadData::ButtonIndex::SQUARE)}},
{SDL_CONTROLLER_BUTTON_Y, {InputBinding(PadData::ButtonIndex::TRIANGLE)}},
{SDL_CONTROLLER_BUTTON_LEFTSTICK, {InputBinding(PadData::ButtonIndex::L3)}},
{SDL_CONTROLLER_BUTTON_RIGHTSTICK, {InputBinding(PadData::ButtonIndex::R3)}},
{SDL_CONTROLLER_BUTTON_BACK, {InputBinding(PadData::ButtonIndex::SELECT)}},
{SDL_CONTROLLER_BUTTON_START, {InputBinding(PadData::ButtonIndex::START)}},
{SDL_CONTROLLER_BUTTON_LEFTSHOULDER, {InputBinding(PadData::ButtonIndex::L1)}},
{SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, {InputBinding(PadData::ButtonIndex::R1)}},
{SDL_CONTROLLER_BUTTON_DPAD_UP, {InputBinding(PadData::ButtonIndex::DPAD_UP)}},
{SDL_CONTROLLER_BUTTON_DPAD_DOWN, {InputBinding(PadData::ButtonIndex::DPAD_DOWN)}},
{SDL_CONTROLLER_BUTTON_DPAD_LEFT, {InputBinding(PadData::ButtonIndex::DPAD_LEFT)}},
{SDL_CONTROLLER_BUTTON_DPAD_RIGHT, {InputBinding(PadData::ButtonIndex::DPAD_RIGHT)}}});
const InputBindingGroups DEFAULT_KEYBOARD_BINDS =
InputBindingGroups(KEYBOARD,
{{SDLK_a, {InputBinding(PadData::AnalogIndex::LEFT_X, true)}},
{SDLK_d, {InputBinding(PadData::AnalogIndex::LEFT_X)}},
{SDLK_s, {InputBinding(PadData::AnalogIndex::LEFT_Y)}},
{SDLK_w, {InputBinding(PadData::AnalogIndex::LEFT_Y, true)}},
{SDLK_l, {InputBinding(PadData::AnalogIndex::RIGHT_X, true)}},
{SDLK_j, {InputBinding(PadData::AnalogIndex::RIGHT_X)}},
{SDLK_k, {InputBinding(PadData::AnalogIndex::RIGHT_Y)}},
{SDLK_i, {InputBinding(PadData::AnalogIndex::RIGHT_Y, true)}}},
{},
{{SDLK_SPACE, {InputBinding(PadData::ButtonIndex::CROSS)}},
{SDLK_e, {InputBinding(PadData::ButtonIndex::CIRCLE)}},
{SDLK_f, {InputBinding(PadData::ButtonIndex::SQUARE)}},
{SDLK_r, {InputBinding(PadData::ButtonIndex::TRIANGLE)}},
{SDLK_COMMA, {InputBinding(PadData::ButtonIndex::L3)}},
{SDLK_PERIOD, {InputBinding(PadData::ButtonIndex::R3)}},
{SDLK_QUOTE, {InputBinding(PadData::ButtonIndex::SELECT)}},
{SDLK_RETURN, {InputBinding(PadData::ButtonIndex::START)}},
{SDLK_q, {InputBinding(PadData::ButtonIndex::L1)}},
{SDLK_o, {InputBinding(PadData::ButtonIndex::R1)}},
{SDLK_1, {InputBinding(PadData::ButtonIndex::L2)}},
{SDLK_p, {InputBinding(PadData::ButtonIndex::R2)}},
{SDLK_UP, {InputBinding(PadData::ButtonIndex::DPAD_UP)}},
{SDLK_DOWN, {InputBinding(PadData::ButtonIndex::DPAD_DOWN)}},
{SDLK_LEFT, {InputBinding(PadData::ButtonIndex::DPAD_LEFT)}},
{SDLK_RIGHT, {InputBinding(PadData::ButtonIndex::DPAD_RIGHT)}}});
const InputBindingGroups DEFAULT_MOUSE_BINDS = InputBindingGroups(MOUSE, {}, {}, {});
std::vector<InputBindingInfo> InputBindingGroups::lookup_analog_binds(PadData::AnalogIndex idx,
bool only_minimum_binds) {
// First see if it's in the cache, if it is return it
if (m_analog_lookup.find({idx, only_minimum_binds}) != m_analog_lookup.end()) {
return m_analog_lookup.at({idx, only_minimum_binds});
}
// Didn't find it, let's construct the cache entry
std::vector<InputBindingInfo> entry = {};
for (const auto& [sdl_code, binds] : analog_axii) {
for (const auto& bind : binds) {
if (bind.pad_data_index != idx || bind.minimum_in_range != only_minimum_binds) {
continue;
}
InputBindingInfo new_info;
switch (device_type) {
case KEYBOARD:
new_info = InputBindingInfo(bind, device_type, sdl_code, false);
break;
default:
new_info.host_name = "TODO - NON-KB ANALOG BIND LOOKUP";
break;
}
entry.push_back(new_info);
}
}
m_analog_lookup[{idx, only_minimum_binds}] = entry;
return entry;
}
std::vector<InputBindingInfo> InputBindingGroups::lookup_button_binds(PadData::ButtonIndex idx) {
// First see if it's in the cache, if it is return it
if (m_button_lookup.find({idx, true}) != m_button_lookup.end()) {
return m_button_lookup.at({idx, true});
}
// Didn't find it, let's construct the cache entry
std::vector<InputBindingInfo> entry = {};
for (const auto& [sdl_code, binds] : buttons) {
for (const auto& bind : binds) {
if (bind.pad_data_index != idx) {
continue;
}
InputBindingInfo new_info(bind, device_type, sdl_code, false);
entry.push_back(new_info);
}
}
for (const auto& [sdl_code, binds] : button_axii) {
for (const auto& bind : binds) {
if (bind.pad_data_index != idx) {
continue;
}
InputBindingInfo new_info(bind, device_type, sdl_code, true);
entry.push_back(new_info);
}
}
m_button_lookup[{idx, true}] = entry;
return entry;
}
void InputBindingGroups::remove_multiple_binds(
u32 sdl_idx,
InputBindAssignmentMeta& bind_meta,
std::unordered_map<u32, std::vector<InputBinding>>& bind_map) {
for (auto it = bind_map.begin(); it != bind_map.end();) {
if (it->first == sdl_idx) {
it++;
continue;
}
bool found_match = false;
for (const auto& bind : it->second) {
if (bind.pad_data_index != bind_meta.pad_idx ||
bind.minimum_in_range != bind_meta.for_analog_minimum) {
continue;
}
it = bind_map.erase(it);
found_match = true;
break;
}
if (!found_match) {
it++;
}
}
}
std::optional<std::pair<InputBinding, bool>> InputBindingGroups::find_button_bind_from_sdl_idx(
u32 sdl_idx,
const std::optional<InputModifiers> modifiers) {
// We need to check that there isn't a shared SDL key on the button group side
for (const auto& [sdl_code, binds] : buttons) {
if (sdl_code == sdl_idx) {
for (const auto bind : binds) {
if (!modifiers || bind.modifiers == modifiers.value()) {
std::pair<InputBinding, bool> result = {bind, false};
return result;
}
}
}
}
for (const auto& [sdl_code, binds] : button_axii) {
if (sdl_code == sdl_idx) {
for (const auto bind : binds) {
if (!modifiers || bind.modifiers == modifiers.value()) {
std::pair<InputBinding, bool> result = {bind, true};
return result;
}
}
}
}
return {};
}
void InputBindingGroups::assign_analog_bind(u32 sdl_idx,
InputBindAssignmentMeta& bind_meta,
const std::optional<InputModifiers> modifiers) {
// Find out if the PS2 input is already bound, if it is we will do a swap so no input is ever left
// unmapped
const auto current_analog_binds =
lookup_analog_binds((PadData::AnalogIndex)bind_meta.pad_idx, bind_meta.for_analog_minimum);
const auto current_button_bind = find_button_bind_from_sdl_idx(sdl_idx, modifiers);
if (analog_axii.find(sdl_idx) != analog_axii.end()) {
const auto existing_binds = analog_axii.at(sdl_idx);
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
bind_meta.for_analog_minimum, modifiers)};
// there already a bind, so swap (as long as it's not the same key)
if (!current_analog_binds.empty() && (u32)current_analog_binds.front().sdl_idx != sdl_idx) {
analog_axii[current_analog_binds.front().sdl_idx] = existing_binds;
}
} else if (!current_analog_binds.empty() && current_button_bind.has_value()) {
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
bind_meta.for_analog_minimum, modifiers)};
if (current_button_bind->second) {
// button_axii
button_axii.erase(sdl_idx);
button_axii[current_analog_binds.front().sdl_idx] = {current_button_bind->first};
} else {
// button
buttons.erase(sdl_idx);
buttons[current_analog_binds.front().sdl_idx] = {current_button_bind->first};
}
} else {
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
bind_meta.for_analog_minimum, modifiers)};
}
remove_multiple_binds(sdl_idx, bind_meta, analog_axii);
// Invalidate lookup cache
m_analog_lookup.clear();
m_button_lookup.clear();
bind_meta.assigned = true;
}
std::optional<std::pair<InputBinding, bool>> InputBindingGroups::find_analog_bind_from_sdl_idx(
u32 sdl_idx,
const std::optional<InputModifiers> modifiers) {
// We need to check that there isn't a shared SDL key on the button group side
for (const auto& [sdl_code, binds] : analog_axii) {
if (sdl_code == sdl_idx) {
for (const auto bind : binds) {
if (!modifiers || bind.modifiers == modifiers.value()) {
std::pair<InputBinding, bool> result = {bind, false};
return result;
}
}
}
}
return {};
}
void InputBindingGroups::assign_button_bind(u32 sdl_idx,
InputBindAssignmentMeta& bind_meta,
const bool analog_button,
const std::optional<InputModifiers> modifiers) {
// Find out if the PS2 input is already bound, if it is we will do a swap so no input is ever left
// unmapped
const auto current_button_binds = lookup_button_binds((PadData::ButtonIndex)bind_meta.pad_idx);
const auto current_analog_bind = find_analog_bind_from_sdl_idx(sdl_idx, modifiers);
if (!analog_button && buttons.find(sdl_idx) != buttons.end()) {
const auto existing_binds = buttons.at(sdl_idx);
if (analog_button) {
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
} else {
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
}
// there already a bind, so swap (as long as it's not the same key)
if (!current_button_binds.empty() && current_button_binds.front().sdl_idx != (s32)sdl_idx) {
if (current_button_binds.front().analog_button) {
button_axii[current_button_binds.front().sdl_idx] = existing_binds;
} else {
buttons[current_button_binds.front().sdl_idx] = existing_binds;
}
}
} else if (analog_button && button_axii.find(sdl_idx) != button_axii.end()) {
// TODO - cleanup this duplication
const auto existing_binds = button_axii.at(sdl_idx);
if (analog_button) {
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
} else {
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
}
// there already a bind, so swap (as long as it's not the same key)
if (!current_button_binds.empty() && current_button_binds.front().sdl_idx != (s32)sdl_idx) {
if (current_button_binds.front().analog_button) {
button_axii[current_button_binds.front().sdl_idx] = existing_binds;
} else {
buttons[current_button_binds.front().sdl_idx] = existing_binds;
}
}
} else if (!current_button_binds.empty() && current_analog_bind.has_value()) {
if (analog_button) {
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
} else {
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
}
analog_axii.erase(sdl_idx);
analog_axii[current_button_binds.front().sdl_idx] = {current_analog_bind->first};
} else {
if (analog_button) {
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
} else {
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
}
}
remove_multiple_binds(sdl_idx, bind_meta, buttons);
remove_multiple_binds(sdl_idx, bind_meta, button_axii);
// Invalidate lookup cache
m_button_lookup.clear();
m_analog_lookup.clear();
bind_meta.assigned = true;
}
void InputBindingGroups::set_bindings(const InputBindingGroups& binds) {
analog_axii = binds.analog_axii;
button_axii = binds.button_axii;
buttons = binds.buttons;
m_analog_lookup.clear();
m_button_lookup.clear();
}
InputBindingInfo::InputBindingInfo(const InputBinding bind,
const InputDeviceType device_type,
const s32 sdl_code,
const bool _analog_button)
: sdl_idx(sdl_code),
pad_idx(bind.pad_data_index),
analog_button(_analog_button),
modifiers(bind.modifiers) {
switch (device_type) {
case CONTROLLER:
if (analog_button) {
host_name = sdl_util::get_controller_axis_name(sdl_code);
} else {
host_name = sdl_util::get_controller_button_name(sdl_code);
}
break;
case KEYBOARD:
host_name = sdl_util::get_keyboard_button_name(sdl_code, modifiers);
break;
case MOUSE:
host_name = sdl_util::get_mouse_button_name(sdl_code, modifiers);
break;
}
}