Skip to content

Commit

Permalink
tweak(input/five): sanitize input mapper/parameter pairings
Browse files Browse the repository at this point in the history
Fixes #1577
  • Loading branch information
gottfriedleibniz committed Mar 6, 2024
1 parent 62cd1b9 commit b228c35
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 30 deletions.
64 changes: 64 additions & 0 deletions code/components/gta-core-five/include/RageInput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* This file is part of the CitizenFX project - http://citizen.re/
*
* See LICENSE and MENTIONS in the root of the source tree for information
* regarding licensing.
*/

#pragma once

#include <cstdint>

namespace rage
{
static constexpr size_t INPUT_PUSH_TO_TALK = 249;

/// parEnumDefinition can be found at "0C 00 01 00 E0 7D 86 3D" - 0x10
enum ioParameterMask : int32_t
{
IOMT_KEYBOARD = 0x0, // 0xE38B382A
IOMT_MOUSE_AXIS = 0x200, // 0x140D3D7E
IOMT_MOUSE_WHEEL = 0x400, // 0x2AE06A4A
IOMT_MOUSE_BUTTON = 0x800, // 0xEC193160
IOMT_PAD_AXIS = 0x1000, // 0x5A190AB4
IOMT_PAD_INDEX = 0x2000, // 0x6A1EB78C
IOMT_JOYSTICK_POV = 0x4000, // 0x68BA5FB3
IOMT_JOYSTICK_BUTTON = 0x8000, // 0xB0629D0A
IOMT_JOYSTICK_AXIS = 0x10000, // 0x2D02D576
IOMT_PAD_BUTTON = 0x20000, // 0xB1639048
IOMT_JOYSTICK_AXIS_NEGATIVE = 0x40000, // 0xFC6BC167
IOMT_JOYSTICK_AXIS_POSITIVE = 0x80000, // 0x0722DC0F
};

enum ioMapperSource : int32_t
{
IOMS_UNDEFINED = -1,
IOMS_KEYBOARD = 0,
IOMS_MOUSE_ABSOLUTEAXIS = 1,
IOMS_MOUSE_CENTEREDAXIS = 2,
IOMS_MOUSE_RELATIVEAXIS = 3,
IOMS_MOUSE_SCALEDAXIS = 4,
IOMS_MOUSE_NORMALIZED = 5,
IOMS_MOUSE_WHEEL = 6,
IOMS_MOUSE_BUTTON = 7,
IOMS_MOUSE_BUTTONANY = 8,
IOMS_PAD_DIGITALBUTTON = 9,
IOMS_PAD_DIGITALBUTTONANY = 10,
IOMS_PAD_ANALOGBUTTON = 11,
IOMS_PAD_AXIS = 12,
IOMS_JOYSTICK_BUTTON = 13,
IOMS_JOYSTICK_AXIS = 14,
IOMS_JOYSTICK_IAXIS = 15,
IOMS_JOYSTICK_AXIS_NEGATIVE = 16,
IOMS_JOYSTICK_AXIS_POSITIVE = 17,
IOMS_JOYSTICK_POV = 18,
IOMS_JOYSTICK_POV_AXIS = 19,
IOMS_PAD_DEBUGBUTTON = 20,
IOMS_DIGITALBUTTON_AXIS = 21,
IOMS_MKB_AXIS = 22,
IOMS_TOUCHPAD_ABSOLUTE_AXIS = 23,
IOMS_TOUCHPAD_CENTERED_AXIS = 24,
IOMS_GAME_CONTROLLED = 25, // Added in prior to b1604
IOMS_FORCE32 = 2147483647,
};
}
131 changes: 101 additions & 30 deletions code/components/gta-core-five/src/GameInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <CoreConsole.h>

#include <RageParser.h>
#include <RageInput.h>

#include <gameSkeleton.h>
#include <ICoreGameInit.h>
Expand All @@ -27,17 +28,17 @@ namespace rage
{
struct ioInputSource
{
uint32_t source;
rage::ioMapperSource source;
uint32_t parameter;
int unk;

inline ioInputSource()
: source(0), parameter(0), unk(-1)
: source(IOMS_KEYBOARD), parameter(0), unk(-1)
{

}

inline ioInputSource(uint32_t source, uint32_t parameter, int unk)
inline ioInputSource(rage::ioMapperSource source, uint32_t parameter, int unk)
: source(source), parameter(parameter), unk(unk)
{

Expand Down Expand Up @@ -92,12 +93,17 @@ namespace rage
//
// Given a source/parameter, gets the parameter to use when serializing a control.
//
static uint32_t GetParameterIndex(uint32_t source, uint32_t parameter);
static uint32_t GetParameterIndex(rage::ioMapperSource source, uint32_t parameter);

//
// Given a source/serialized parameter, gets the native parameter to use.
//
static uint32_t UngetParameterIndex(uint32_t source, uint32_t parameter);
static uint32_t UngetParameterIndex(rage::ioMapperSource source, uint32_t parameter);

//
// Ensure the <source, parameter> pairing form a valid matching.
//
static bool ValidateParameter(rage::ioMapperSource source, uint32_t parameter);
};
}

Expand Down Expand Up @@ -171,12 +177,12 @@ uint32_t ControlSourceToMapperSource(uint32_t source)
return _controlSourceToMapperSource(source);
}

static hook::cdecl_stub<uint32_t(uint32_t, uint32_t)> _getParameterIndex([]()
static hook::cdecl_stub<uint32_t(rage::ioMapperSource, uint32_t)> _getParameterIndex([]()
{
return hook::get_pattern("83 F9 01 74 2C 0F 8E E8 00 00 00", -0xF);
});

uint32_t rage::ioMapper::GetParameterIndex(uint32_t source, uint32_t parameter)
uint32_t rage::ioMapper::GetParameterIndex(rage::ioMapperSource source, uint32_t parameter)
{
return _getParameterIndex(source, parameter);
}
Expand All @@ -186,11 +192,63 @@ static hook::cdecl_stub<uint32_t(uint32_t, uint32_t)> _ungetParameterIndex([]()
return hook::get_pattern("74 28 7E 36 83 F9", -0x12);
});

uint32_t rage::ioMapper::UngetParameterIndex(uint32_t source, uint32_t parameter)
uint32_t rage::ioMapper::UngetParameterIndex(rage::ioMapperSource source, uint32_t parameter)
{
return _ungetParameterIndex(source, parameter);
}

// GH-1577: Sanitize invalid mapper/parameter pairings. In the reported case
// IOMS_PAD_DIGITALBUTTON may cause invalid keys to overflow into being valid.
// This function attempts to rebuild the switch statement from _getParameterIndex.
bool rage::ioMapper::ValidateParameter(rage::ioMapperSource source, uint32_t parameter)
{
switch (source)
{
case IOMS_KEYBOARD:
return (parameter & IOMT_KEYBOARD) == IOMT_KEYBOARD;
case IOMS_MOUSE_ABSOLUTEAXIS:
return (parameter & IOMT_MOUSE_AXIS) == IOMT_MOUSE_AXIS;
case IOMS_MOUSE_CENTEREDAXIS:
case IOMS_MOUSE_RELATIVEAXIS:
case IOMS_MOUSE_SCALEDAXIS:
case IOMS_MOUSE_NORMALIZED:
return (parameter & IOMT_MOUSE_AXIS) == IOMT_MOUSE_AXIS;
case IOMS_MOUSE_WHEEL:
return (parameter & IOMT_MOUSE_WHEEL) == IOMT_MOUSE_WHEEL;
case IOMS_MOUSE_BUTTON:
return (parameter & IOMT_MOUSE_BUTTON) == IOMT_MOUSE_BUTTON;
case IOMS_MOUSE_BUTTONANY:
case IOMS_PAD_DIGITALBUTTONANY:
return true;
case IOMS_PAD_DIGITALBUTTON:
case IOMS_PAD_DEBUGBUTTON:
return (parameter & IOMT_PAD_BUTTON) == IOMT_PAD_BUTTON;
case IOMS_PAD_ANALOGBUTTON:
return (parameter & IOMT_PAD_INDEX) == IOMT_PAD_INDEX;
case IOMS_PAD_AXIS:
return (parameter & IOMT_PAD_AXIS) == IOMT_PAD_AXIS;
case IOMS_JOYSTICK_BUTTON:
return (parameter & IOMT_JOYSTICK_BUTTON) == IOMT_JOYSTICK_BUTTON;
case IOMS_JOYSTICK_AXIS:
case IOMS_JOYSTICK_IAXIS:
case IOMS_JOYSTICK_AXIS_NEGATIVE:
case IOMS_JOYSTICK_AXIS_POSITIVE:
return (parameter & IOMT_JOYSTICK_AXIS) == IOMT_JOYSTICK_AXIS;
case IOMS_JOYSTICK_POV:
case IOMS_JOYSTICK_POV_AXIS:
return (parameter & IOMT_JOYSTICK_POV) == IOMT_JOYSTICK_POV;
case IOMS_MKB_AXIS:
return (parameter & IOMT_KEYBOARD) == IOMT_KEYBOARD
|| (parameter & IOMT_MOUSE_BUTTON) == IOMT_MOUSE_BUTTON
|| (parameter & IOMT_MOUSE_WHEEL) == IOMT_MOUSE_WHEEL;
case IOMS_TOUCHPAD_ABSOLUTE_AXIS:
case IOMS_TOUCHPAD_CENTERED_AXIS:
return (parameter & IOMT_MOUSE_AXIS) == IOMT_MOUSE_AXIS;
default:
return false;
}
}

class CControl
{
public:
Expand Down Expand Up @@ -274,13 +332,13 @@ Button::Button(const std::string& name)

m_downCommand = std::make_unique<ConsoleCommand>("+" + name, [this]()
{
rage::ioInputSource source{ 0, 1, -4 }; // keyboard?
rage::ioInputSource source{ rage::IOMS_KEYBOARD, 1, -4 }; // keyboard?
UpdateIoValues(1.0f, source);
});

m_upCommand = std::make_unique<ConsoleCommand>("-" + name, [this]()
{
rage::ioInputSource source{ 0, 1, -4 }; // keyboard?
rage::ioInputSource source{ rage::IOMS_KEYBOARD, 1, -4 }; // keyboard?
UpdateIoValues(0.0f, source);
});
}
Expand All @@ -298,7 +356,7 @@ void Button::UpdateOnControl()
{
if (copy)
{
rage::ioInputSource source{ 0, 1, -4 }; // keyboard?
rage::ioInputSource source{ rage::IOMS_KEYBOARD, 1, -4 };

if (!copy->IsDown(0.5f, rage::ioValue::NO_DEAD_ZONE))
{
Expand Down Expand Up @@ -350,7 +408,7 @@ class Binding

bool IsActive() const;

inline void SetBinding(std::tuple<int, int> binding)
inline void SetBinding(std::tuple<rage::ioMapperSource, int> binding)
{
m_binding = binding;
}
Expand Down Expand Up @@ -389,7 +447,7 @@ class Binding

std::string m_tag;

std::tuple<int, int> m_binding;
std::tuple<rage::ioMapperSource, int> m_binding;

std::set<rage::ioMapper*> m_mappers;
};
Expand Down Expand Up @@ -577,7 +635,7 @@ class BindingManager

void Update(rage::ioMapper* mapper, uint32_t time);

std::shared_ptr<Binding> Bind(int source, int parameter, const std::string& command);
std::shared_ptr<Binding> Bind(rage::ioMapperSource source, int parameter, const std::string& command);

inline auto& GetBindings()
{
Expand All @@ -596,14 +654,14 @@ class BindingManager
std::unique_ptr<ConsoleCommand> m_unbindAllCommand;
std::unique_ptr<ConsoleCommand> m_listBindsCommand;

std::multimap<std::tuple<int, int>, std::shared_ptr<Binding>> m_bindings;
std::multimap<std::tuple<rage::ioMapperSource, int>, std::shared_ptr<Binding>> m_bindings;

std::list<std::unique_ptr<Button>> m_buttons;

concurrency::concurrent_queue<std::function<void()>> m_queue;
};

static std::map<std::string, int, console::IgnoreCaseLess> ioSourceMap;
static std::map<std::string, rage::ioMapperSource, console::IgnoreCaseLess> ioSourceMap;
static std::map<std::string, int, console::IgnoreCaseLess> ioParameterMap;

void BindingManager::Initialize()
Expand Down Expand Up @@ -644,7 +702,7 @@ void BindingManager::Initialize()

auto bind = [=](const std::string& ioSourceName, const std::string& ioParameterName, const std::string& commandString, const std::string& tag)
{
int ioSource;
rage::ioMapperSource ioSource;

{
auto it = ioSourceMap.find(ioSourceName);
Expand All @@ -668,6 +726,11 @@ void BindingManager::Initialize()
console::Printf("IO", "Invalid key name %s\n", ioParameterName.c_str());
return;
}
else if (!rage::ioMapper::ValidateParameter(ioSource, it->second))
{
console::Printf("IO", "Invalid I/O parameter pairing: <%s, %s>\n", ioSourceName, ioParameterName);
return;
}

ioParameter = rage::ioMapper::UngetParameterIndex(ioSource, it->second);
}
Expand Down Expand Up @@ -704,7 +767,7 @@ void BindingManager::Initialize()

m_unbindCommand = std::make_unique<ConsoleCommand>("unbind", [=](const std::string& ioSourceName, const std::string& ioParameterName)
{
int ioSource;
rage::ioMapperSource ioSource;

{
auto it = ioSourceMap.find(ioSourceName);
Expand All @@ -728,6 +791,11 @@ void BindingManager::Initialize()
console::Printf("IO", "Invalid key name %s\n", ioParameterName.c_str());
return;
}
else if (!rage::ioMapper::ValidateParameter(ioSource, it->second))
{
console::Printf("IO", "Invalid I/O parameter pairing: <%s, %s>\n", ioSourceName, ioParameterName);
return;
}

ioParameter = rage::ioMapper::UngetParameterIndex(ioSource, it->second);
}
Expand All @@ -743,7 +811,7 @@ void BindingManager::Initialize()
});
}

std::shared_ptr<Binding> BindingManager::Bind(int ioSource, int ioParameter, const std::string& commandString)
std::shared_ptr<Binding> BindingManager::Bind(rage::ioMapperSource ioSource, int ioParameter, const std::string& commandString)
{
for (auto it = m_bindings.begin(); it != m_bindings.end(); )
{
Expand Down Expand Up @@ -866,7 +934,7 @@ void BindingManager::OnGameInit()
std::string_view thisName = *name;
thisName = thisName.substr(thisName.find_first_of('_') + 1);

ioSourceMap[std::string(thisName)] = field->index;
ioSourceMap[std::string(thisName)] = static_cast<rage::ioMapperSource>(field->index);

name++;
}
Expand Down Expand Up @@ -981,7 +1049,7 @@ namespace game

if (!ioms.empty() && !ioParam.empty())
{
int ioSource;
rage::ioMapperSource ioSource;

{
auto it = ioSourceMap.find(ioms);
Expand All @@ -1005,6 +1073,11 @@ namespace game
console::Printf("IO", "Invalid key name %s\n", ioParam);
return;
}
else if (!rage::ioMapper::ValidateParameter(ioSource, it->second))
{
console::Printf("IO", "Invalid I/O parameter pairing: <%s, %s>\n", ioms, ioParam);
return;
}

ioParameter = rage::ioMapper::UngetParameterIndex(ioSource, it->second);
}
Expand All @@ -1024,7 +1097,7 @@ namespace game
bool IsInputSourceDown(const rage::ioInputSource& controlData)
{
// mouse?
if (controlData.source == 7)
if (controlData.source == rage::IOMS_MOUSE_BUTTON)
{
return InputHook::IsMouseButtonDown(controlData.parameter);
}
Expand Down Expand Up @@ -1099,7 +1172,7 @@ static void* GetBindingForControl(void* control, rage::ioInputSource* outBinding
if (controlId & 0x80000000)
{
outBinding->parameter = -1;
outBinding->source = -1;
outBinding->source = rage::IOMS_UNDEFINED;
outBinding->unk = -1;

auto controlRef = GetRegisteredBindingByHash(controlId);
Expand Down Expand Up @@ -1228,19 +1301,17 @@ static void MapFuncHook(void* a1, uint32_t controlIdx, void* a3, void* a4)

void UpdateButtonPlumbing()
{
constexpr size_t INPUT_PUSH_TO_TALK = 249;

rage::ioInputSource controlDatas[2];
CControlMgr::GetPlayerControls()->GetBinding(controlDatas[0], INPUT_PUSH_TO_TALK, -1, false, false);
CControlMgr::GetPlayerControls()->GetBinding(controlDatas[1], INPUT_PUSH_TO_TALK, -1, true, false);
CControlMgr::GetPlayerControls()->GetBinding(controlDatas[0], rage::INPUT_PUSH_TO_TALK, -1, false, false);
CControlMgr::GetPlayerControls()->GetBinding(controlDatas[1], rage::INPUT_PUSH_TO_TALK, -1, true, false);

auto mapBypass = [](rage::ioInputSource& controlData)
{
InputHook::ControlBypass bypass = { 0 };

if (controlData.source == 0 /* IOMS_KEYBOARD */ || controlData.source == 7 /* IOMS_MOUSE_BUTTON */)

if (controlData.source == rage::IOMS_KEYBOARD || controlData.source == rage::IOMS_MOUSE_BUTTON)
{
bypass.isMouse = controlData.source == 7;
bypass.isMouse = controlData.source == rage::IOMS_MOUSE_BUTTON;
bypass.ctrlIdx = controlData.parameter;
}

Expand Down

0 comments on commit b228c35

Please sign in to comment.