Write gamepad axis configuration to config file

This commit is contained in:
Jan 2021-08-22 16:07:23 +02:00
parent dcbb120de1
commit 8cd99235c0
4 changed files with 270 additions and 41 deletions

View File

@ -45,6 +45,32 @@ namespace Game
GPAD_INVALID
};
const char* physicalAxisNames[GPAD_PHYSAXIS_COUNT]
{
"A_RSTICK_X",
"A_RSTICK_Y",
"A_LSTICK_X",
"A_LSTICK_Y",
"A_RTRIGGER",
"A_LTRIGGER"
};
const char* virtualAxisNames[GPAD_VIRTAXIS_COUNT]
{
"VA_SIDE",
"VA_FORWARD",
"VA_UP",
"VA_YAW",
"VA_PITCH",
"VA_ATTACK"
};
const char* gamePadMappingTypeNames[GPAD_MAP_COUNT]
{
"MAP_LINEAR",
"MAP_SQUARED"
};
keyNum_t menuScrollButtonList[]
{
K_APAD_UP,
@ -80,7 +106,6 @@ namespace Game
keyname_t combinedKeyNames[VANILLA_KEY_NAME_COUNT + std::extent_v<decltype(extendedKeyNames)> + 1];
GpadAxesGlob gaGlobs[MAX_GAMEPADS];
PlayerKeyState* playerKeys = reinterpret_cast<PlayerKeyState*>(0xA1B7D0);
keyname_t* vanillaKeyNames = reinterpret_cast<keyname_t*>(0x798580);
}
@ -88,7 +113,8 @@ namespace Game
namespace Components
{
Gamepad::GamePad Gamepad::gamePads[Game::MAX_GAMEPADS]{};
Gamepad::GamePadGlobals Gamepad::gamePadGlobals[Game::MAX_GAMEPADS]{};
Gamepad::GamePadGlobals Gamepad::gamePadGlobals[Game::MAX_GAMEPADS]{{}};
int Gamepad::gamePadBindingsModifiedFlags = 0;
std::chrono::milliseconds Gamepad::timeAtFirstHeldMaxLookX = 0ms; // "For how much time in milliseconds has the player been holding a horizontal direction on their stick, fully" (-1.0 or 1.0)
bool Gamepad::isHoldingMaxLookX = false;
bool Gamepad::isADS;
@ -161,6 +187,18 @@ namespace Components
Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_UP, "actionslot 4"),
};
Gamepad::GamePadGlobals::GamePadGlobals()
: axes{},
nextScrollTime(0)
{
for (auto& virtualAxis : axes.virtualAxes)
{
virtualAxis.physicalAxis = Game::GPAD_PHYSAXIS_NONE;
virtualAxis.mapType = Game::GPAD_MAP_NONE;
}
}
// Same thing
// void Gamepad::Vibrate(int leftVal, int rightVal)
@ -549,9 +587,9 @@ namespace Components
return;
const auto scrollDelayFirst = gpad_menu_scroll_delay_first.get<int>();
for(const auto scrollButton : Game::menuScrollButtonList)
for (const auto scrollButton : Game::menuScrollButtonList)
{
if(key == scrollButton)
if (key == scrollButton)
{
gamePadGlobal.nextScrollTime = scrollDelayFirst + time;
return;
@ -568,46 +606,48 @@ namespace Components
const auto stick = Game::stickForAxis[physicalAxis];
const auto stickIndex = stick & Game::GPAD_VALUE_MASK;
if(stick != Game::GPAD_INVALID)
if (stick != Game::GPAD_INVALID)
{
assert(stickIndex < 4);
const auto& mapping = Game::analogStickList[stickIndex];
if(gamePad.stickDown[stickIndex][Game::GPAD_STICK_POS])
if (gamePad.stickDown[stickIndex][Game::GPAD_STICK_POS])
{
const Game::GamePadButtonEvent event = gamePad.stickDownLast[stickIndex][Game::GPAD_STICK_POS] ? Game::GPAD_BUTTON_UPDATE : Game::GPAD_BUTTON_PRESSED;
CL_GamepadButtonEvent(gamePadIndex, mapping.posCode, event, time, Game::GPAD_NONE);
}
else if(gamePad.stickDown[stickIndex][Game::GPAD_STICK_NEG])
else if (gamePad.stickDown[stickIndex][Game::GPAD_STICK_NEG])
{
const Game::GamePadButtonEvent event = gamePad.stickDownLast[stickIndex][Game::GPAD_STICK_NEG] ? Game::GPAD_BUTTON_UPDATE : Game::GPAD_BUTTON_PRESSED;
CL_GamepadButtonEvent(gamePadIndex, mapping.negCode, event, time, Game::GPAD_NONE);
}
else if(gamePad.stickDownLast[stickIndex][Game::GPAD_STICK_POS])
else if (gamePad.stickDownLast[stickIndex][Game::GPAD_STICK_POS])
{
CL_GamepadButtonEvent(gamePadIndex, mapping.posCode, Game::GPAD_BUTTON_RELEASED, time, Game::GPAD_NONE);
}
else if(gamePad.stickDownLast[stickIndex][Game::GPAD_STICK_NEG])
else if (gamePad.stickDownLast[stickIndex][Game::GPAD_STICK_NEG])
{
CL_GamepadButtonEvent(gamePadIndex, mapping.negCode, Game::GPAD_BUTTON_RELEASED, time, Game::GPAD_NONE);
}
}
}
void Gamepad::CL_GamepadEvent(int gamePadIndex, const Game::GamepadPhysicalAxis physicalAxis, const float value, const unsigned time)
void Gamepad::CL_GamepadEvent(const int gamePadIndex, const Game::GamepadPhysicalAxis physicalAxis, const float value, const unsigned time)
{
assert(gamePadIndex < Game::MAX_GAMEPADS);
assert(physicalAxis < Game::GPAD_PHYSAXIS_COUNT && physicalAxis >= 0);
Game::gaGlobs[gamePadIndex].axesValues[physicalAxis] = value;
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
gamePadGlobal.axes.axesValues[physicalAxis] = value;
CL_GamepadGenerateAPad(gamePadIndex, physicalAxis, time);
}
void Gamepad::UI_GamepadKeyEvent(const int gamePadIndex, const int key, const bool down)
{
for(const auto& mapping : controllerMenuKeyMappings)
for (const auto& mapping : controllerMenuKeyMappings)
{
if(mapping.controllerKey == key)
if (mapping.controllerKey == key)
{
Game::UI_KeyEvent(gamePadIndex, mapping.pcKey, down);
return;
@ -623,22 +663,22 @@ namespace Components
assert(gamePadIndex < Game::MAX_GAMEPADS);
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
if(Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
{
const int scrollDelayFirst = gpad_menu_scroll_delay_first.get<int>();
const int scrollDelayRest = gpad_menu_scroll_delay_rest.get<int>();
for(const auto menuScrollButton : Game::menuScrollButtonList)
for (const auto menuScrollButton : Game::menuScrollButtonList)
{
if(key == menuScrollButton)
if (key == menuScrollButton)
{
if(repeatCount == 1)
if (repeatCount == 1)
{
gamePadGlobal.nextScrollTime = time + scrollDelayFirst;
return false;
}
if(time > gamePadGlobal.nextScrollTime)
if (time > gamePadGlobal.nextScrollTime)
{
gamePadGlobal.nextScrollTime = time + scrollDelayRest;
return false;
@ -661,12 +701,12 @@ namespace Components
auto& keyState = Game::playerKeys[gamePadIndex];
keyState.keys[key].down = pressedOrUpdated;
if(pressedOrUpdated)
if (pressedOrUpdated)
{
if (++keyState.keys[key].repeats == 1)
keyState.anyKeyDown++;
}
else if(buttonEvent == Game::GPAD_BUTTON_RELEASED && keyState.keys[key].repeats > 0)
else if (buttonEvent == Game::GPAD_BUTTON_RELEASED && keyState.keys[key].repeats > 0)
{
keyState.keys[key].repeats = 0;
if (--keyState.anyKeyDown < 0)
@ -676,13 +716,13 @@ namespace Components
if (pressedOrUpdated && CL_CheckForIgnoreDueToRepeat(gamePadIndex, key, keyState.keys[key].repeats, time))
return;
if(Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated)
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated)
{
if(key == Game::K_BUTTON_B || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+actionslot 4") == 0)
if (key == Game::K_BUTTON_B || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+actionslot 4") == 0)
{
keyState.locSelInputState = Game::LOC_SEL_INPUT_CANCEL;
}
else if(key == Game::K_BUTTON_A || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+attack") == 0)
else if (key == Game::K_BUTTON_A || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+attack") == 0)
{
keyState.locSelInputState = Game::LOC_SEL_INPUT_CONFIRM;
}
@ -692,9 +732,9 @@ namespace Components
keyState.locSelInputState = Game::LOC_SEL_INPUT_NONE;
const auto* keyBinding = keyState.keys[key].binding;
char cmd[1024];
if(pressedOrUpdated)
if (pressedOrUpdated)
{
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
{
@ -789,12 +829,12 @@ namespace Components
float value = 0.0f;
if(button & Game::GPAD_DIGITAL_MASK)
if (button & Game::GPAD_DIGITAL_MASK)
{
const auto buttonValue = button & Game::GPAD_VALUE_MASK;
value = buttonValue & gamePad.digitals ? 1.0f : 0.0f;
}
else if(button & Game::GPAD_ANALOG_MASK)
else if (button & Game::GPAD_ANALOG_MASK)
{
const auto analogIndex = button & Game::GPAD_VALUE_MASK;
if (analogIndex < std::extent_v<decltype(gamePad.analogs)>)
@ -814,18 +854,18 @@ namespace Components
bool down = false;
bool lastDown = false;
if(button & Game::GPAD_DIGITAL_MASK)
if (button & Game::GPAD_DIGITAL_MASK)
{
const auto buttonValue = button & Game::GPAD_VALUE_MASK;
down = (buttonValue & gamePad.digitals) != 0;
lastDown = (buttonValue & gamePad.lastDigitals) != 0;
}
else if(button & Game::GPAD_ANALOG_MASK)
else if (button & Game::GPAD_ANALOG_MASK)
{
const auto analogIndex = button & Game::GPAD_VALUE_MASK;
assert(analogIndex < std::extent_v<decltype(gamePad.analogs)>);
if(analogIndex < std::extent_v<decltype(gamePad.analogs)>)
if (analogIndex < std::extent_v<decltype(gamePad.analogs)>)
{
down = gamePad.analogs[analogIndex] > 0.0f;
lastDown = gamePad.lastAnalogs[analogIndex] > 0.0f;
@ -851,7 +891,7 @@ namespace Components
if (button & Game::GPAD_DIGITAL_MASK)
{
const auto buttonValue = button & Game::GPAD_VALUE_MASK;
down = (gamePad.digitals & buttonValue) != 0;
lastDown = (gamePad.lastDigitals & buttonValue) != 0;
}
@ -870,7 +910,7 @@ namespace Components
return !down && lastDown;
}
void Gamepad::GPad_UpdateSticksDown(int gamePadIndex)
void Gamepad::GPad_UpdateSticksDown(const int gamePadIndex)
{
assert(gamePadIndex < Game::MAX_GAMEPADS);
auto& gamePad = gamePads[gamePadIndex];
@ -1012,11 +1052,11 @@ namespace Components
const auto time = Game::Sys_Milliseconds();
bool gpadPresent = false;
for(auto gamePadIndex = 0; gamePadIndex < Game::MAX_GAMEPADS; gamePadIndex++)
for (auto gamePadIndex = 0; gamePadIndex < Game::MAX_GAMEPADS; gamePadIndex++)
{
const auto& gamePad = gamePads[gamePadIndex];
if(gamePad.enabled)
if (gamePad.enabled)
{
gpadPresent = true;
const auto lx = GPad_GetStick(gamePadIndex, Game::GPAD_LX);
@ -1025,7 +1065,7 @@ namespace Components
const auto ry = GPad_GetStick(gamePadIndex, Game::GPAD_RY);
const auto leftTrig = GPad_GetButton(gamePadIndex, Game::GPAD_L_TRIG);
const auto rightTrig = GPad_GetButton(gamePadIndex, Game::GPAD_R_TRIG);
CL_GamepadEvent(gamePadIndex, Game::GPAD_PHYSAXIS_LSTICK_X, lx, time);
CL_GamepadEvent(gamePadIndex, Game::GPAD_PHYSAXIS_LSTICK_Y, ly, time);
CL_GamepadEvent(gamePadIndex, Game::GPAD_PHYSAXIS_RSTICK_X, rx, time);
@ -1035,7 +1075,7 @@ namespace Components
for (const auto& buttonMapping : Game::buttonList)
{
if(GPad_IsButtonPressed(gamePadIndex, buttonMapping.padButton))
if (GPad_IsButtonPressed(gamePadIndex, buttonMapping.padButton))
{
CL_GamepadButtonEventForPort(
gamePadIndex,
@ -1044,7 +1084,7 @@ namespace Components
time,
buttonMapping.padButton);
}
else if(GPad_ButtonRequiresUpdates(gamePadIndex, buttonMapping.padButton))
else if (GPad_ButtonRequiresUpdates(gamePadIndex, buttonMapping.padButton))
{
CL_GamepadButtonEventForPort(
gamePadIndex,
@ -1053,7 +1093,7 @@ namespace Components
time,
buttonMapping.padButton);
}
else if(GPad_IsButtonReleased(gamePadIndex, buttonMapping.padButton))
else if (GPad_IsButtonReleased(gamePadIndex, buttonMapping.padButton))
{
CL_GamepadButtonEventForPort(
gamePadIndex,
@ -1078,6 +1118,153 @@ namespace Components
IN_GamePadsMove();
}
void Gamepad::Gamepad_WriteBindings(const int gamePadIndex, const int handle)
{
assert(gamePadIndex < Game::MAX_GAMEPADS);
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
Game::FS_Printf(handle, "unbindallaxis\n");
for(auto virtualAxisIndex = 0u; virtualAxisIndex < Game::GPAD_VIRTAXIS_COUNT; virtualAxisIndex++)
{
const auto& axisMapping = gamePadGlobal.axes.virtualAxes[virtualAxisIndex];
if (axisMapping.physicalAxis <= Game::GPAD_PHYSAXIS_NONE || axisMapping.physicalAxis >= Game::GPAD_PHYSAXIS_COUNT
|| axisMapping.mapType <= Game::GPAD_MAP_NONE || axisMapping.mapType >= Game::GPAD_MAP_COUNT)
{
continue;
}
const auto* physicalAxisName = Game::physicalAxisNames[axisMapping.physicalAxis];
const auto* virtualAxisName = Game::virtualAxisNames[virtualAxisIndex];
const auto* mappingName = Game::gamePadMappingTypeNames[axisMapping.mapType];
Game::FS_Printf(handle, "bindaxis %s %s %s\n", physicalAxisName, virtualAxisName, mappingName);
}
}
void Gamepad::Key_WriteBindings_Hk(const int localClientNum, const int handle)
{
// Call original function
Utils::Hook::Call<void(int, int)>(0x4A5A20)(localClientNum, handle);
Gamepad_WriteBindings(0, handle);
}
void __declspec(naked) Gamepad::Com_WriteConfiguration_Modified_Stub()
{
__asm
{
mov eax, [ecx + 0x18]
or eax, gamePadBindingsModifiedFlags // Also check for gamePadBindingsModifiedFlags
test al, 1
jz endMethod
mov gamePadBindingsModifiedFlags, 0 // Reset gamePadBindingsModifiedFlags
mov eax, [ecx + 0x18] // Restore eax to dvar_modified_flags
push 0x60B26E
retn
endMethod:
push 0x60B298
retn
}
}
void Gamepad::Gamepad_BindAxis(const int gamePadIndex, const Game::GamepadPhysicalAxis realIndex, const Game::GamepadVirtualAxis axisIndex, const Game::GamepadMapping mapType)
{
assert(gamePadIndex < Game::MAX_GAMEPADS);
assert(realIndex > Game::GPAD_PHYSAXIS_NONE && realIndex < Game::GPAD_PHYSAXIS_COUNT);
assert(axisIndex > Game::GPAD_VIRTAXIS_NONE && axisIndex < Game::GPAD_VIRTAXIS_COUNT);
assert(mapType > Game::GPAD_MAP_NONE && mapType < Game::GPAD_MAP_COUNT);
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
gamePadGlobal.axes.virtualAxes[axisIndex].physicalAxis = realIndex;
gamePadGlobal.axes.virtualAxes[axisIndex].mapType = mapType;
gamePadBindingsModifiedFlags |= 1;
}
Game::GamepadPhysicalAxis Gamepad::StringToPhysicalAxis(const char* str)
{
for (auto i = 0u; i < std::extent_v<decltype(Game::physicalAxisNames)>; i++)
{
if (strcmp(str, Game::physicalAxisNames[i]) == 0)
return static_cast<Game::GamepadPhysicalAxis>(i);
}
return Game::GPAD_PHYSAXIS_NONE;
}
Game::GamepadVirtualAxis Gamepad::StringToVirtualAxis(const char* str)
{
for (auto i = 0u; i < std::extent_v<decltype(Game::virtualAxisNames)>; i++)
{
if (strcmp(str, Game::virtualAxisNames[i]) == 0)
return static_cast<Game::GamepadVirtualAxis>(i);
}
return Game::GPAD_VIRTAXIS_NONE;
}
Game::GamepadMapping Gamepad::StringToGamePadMapping(const char* str)
{
for (auto i = 0u; i < std::extent_v<decltype(Game::gamePadMappingTypeNames)>; i++)
{
if (strcmp(str, Game::gamePadMappingTypeNames[i]) == 0)
return static_cast<Game::GamepadMapping>(i);
}
return Game::GPAD_MAP_NONE;
}
void Gamepad::Axis_Bind_f(Command::Params* params)
{
if (params->length() < 4)
{
Logger::Print("bindaxis <real axis> <virtual axis> <input type>\n");
return;
}
const auto* physicalAxisText = params->get(1);
const auto* virtualAxisText = params->get(2);
const auto* mappingText = params->get(3);
const Game::GamepadPhysicalAxis physicalAxis = StringToPhysicalAxis(physicalAxisText);
if (physicalAxis == Game::GPAD_PHYSAXIS_NONE)
{
Logger::Print("\"%s\" isn't a valid physical axis\n", physicalAxisText);
return;
}
const Game::GamepadVirtualAxis virtualAxis = StringToVirtualAxis(virtualAxisText);
if (virtualAxis == Game::GPAD_VIRTAXIS_NONE)
{
Logger::Print("\"%s\" isn't a valid virtual axis\n", virtualAxisText);
return;
}
const Game::GamepadMapping mapping = StringToGamePadMapping(mappingText);
if (mapping == Game::GPAD_MAP_NONE)
{
Logger::Print("\"%s\" isn't a valid input type\n", mappingText);
return;
}
Gamepad_BindAxis(0, physicalAxis, virtualAxis, mapping);
}
void Gamepad::Axis_Unbindall_f(Command::Params* params)
{
auto& gamePadGlobal = gamePadGlobals[0];
for (auto& virtualAxis : gamePadGlobal.axes.virtualAxes)
{
virtualAxis.physicalAxis = Game::GPAD_PHYSAXIS_NONE;
virtualAxis.mapType = Game::GPAD_MAP_NONE;
}
}
void Gamepad::InitDvars()
{
gpad_enabled = Dvar::Register<bool>("gpad_enabled", false, Game::DVAR_FLAG_SAVED, "Game pad enabled");
@ -1111,7 +1298,7 @@ namespace Components
{
memcpy(Game::combinedKeyNames, Game::vanillaKeyNames, sizeof(Game::keyname_t) * Game::VANILLA_KEY_NAME_COUNT);
memcpy(&Game::combinedKeyNames[Game::VANILLA_KEY_NAME_COUNT], Game::extendedKeyNames, sizeof(Game::keyname_t) * std::extent_v<decltype(Game::extendedKeyNames)>);
Game::combinedKeyNames[std::extent_v<decltype(Game::combinedKeyNames)> - 1] = { nullptr, 0 };
Game::combinedKeyNames[std::extent_v<decltype(Game::combinedKeyNames)> - 1] = {nullptr, 0};
Utils::Hook::Set<Game::keyname_t*>(0x4A780A, Game::combinedKeyNames);
Utils::Hook::Set<Game::keyname_t*>(0x4A7810, Game::combinedKeyNames);
@ -1137,8 +1324,16 @@ namespace Components
Utils::Hook(0x492127, MSG_ReadDeltaUsercmdKeyStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x492009, MSG_ReadDeltaUsercmdKeyStub2, HOOK_JUMP).install()->quick();
// Also rewrite configuration when gamepad config is dirty
Utils::Hook(0x60B264, Com_WriteConfiguration_Modified_Stub, HOOK_JUMP).install()->quick();
Utils::Hook(0x60B223, Key_WriteBindings_Hk, HOOK_CALL).install()->quick();
CreateKeyNameMap();
Command::Add("bindaxis", Axis_Bind_f);
Command::Add("unbindallaxis", Axis_Unbindall_f);
if (Dedicated::IsEnabled())
return;

View File

@ -60,6 +60,19 @@ namespace Game
GPAD_PHYSAXIS_COUNT,
};
enum GamepadVirtualAxis
{
GPAD_VIRTAXIS_NONE = -1,
GPAD_VIRTAXIS_SIDE = 0x0,
GPAD_VIRTAXIS_FORWARD = 0x1,
GPAD_VIRTAXIS_UP = 0x2,
GPAD_VIRTAXIS_YAW = 0x3,
GPAD_VIRTAXIS_PITCH = 0x4,
GPAD_VIRTAXIS_ATTACK = 0x5,
GPAD_VIRTAXIS_COUNT
};
enum GamePadStickDir
{
GPAD_STICK_POS = 0x0,
@ -73,7 +86,8 @@ namespace Game
GPAD_MAP_NONE = -1,
GPAD_MAP_LINEAR = 0x0,
GPAD_MAP_SQUARED = 0x1,
GPAD_MAP_COUNT = 0x2
GPAD_MAP_COUNT
};
struct ButtonToCodeMap_t
@ -98,7 +112,7 @@ namespace Game
struct GpadAxesGlob
{
float axesValues[GPAD_PHYSAXIS_COUNT];
GamepadVirtualAxisMapping virtualAxes[GPAD_PHYSAXIS_COUNT];
GamepadVirtualAxisMapping virtualAxes[GPAD_VIRTAXIS_COUNT];
};
}
@ -132,7 +146,10 @@ namespace Components
struct GamePadGlobals
{
Game::GpadAxesGlob axes;
unsigned nextScrollTime;
GamePadGlobals();
};
struct ActionMapping
@ -156,6 +173,8 @@ namespace Components
static GamePad gamePads[Game::MAX_GAMEPADS];
static GamePadGlobals gamePadGlobals[Game::MAX_GAMEPADS];
static int gamePadBindingsModifiedFlags;
static bool isHoldingMaxLookX;
static std::chrono::milliseconds timeAtFirstHeldMaxLookX;
static bool isADS;
@ -236,6 +255,17 @@ namespace Components
static void IN_GamePadsMove();
static void IN_Frame_Hk();
static void Gamepad_WriteBindings(int gamePadIndex, int handle);
static void Key_WriteBindings_Hk(int localClientNum, int handle);
static void Com_WriteConfiguration_Modified_Stub();
static void Gamepad_BindAxis(int gamePadIndex, Game::GamepadPhysicalAxis realIndex, Game::GamepadVirtualAxis axisIndex, Game::GamepadMapping mapType);
static Game::GamepadPhysicalAxis StringToPhysicalAxis(const char* str);
static Game::GamepadVirtualAxis StringToVirtualAxis(const char* str);
static Game::GamepadMapping StringToGamePadMapping(const char* str);
static void Axis_Bind_f(Command::Params* params);
static void Axis_Unbindall_f(Command::Params* params);
static void InitDvars();
static void IN_Init_Hk();

View File

@ -130,6 +130,7 @@ namespace Game
FS_FCloseFile_t FS_FCloseFile = FS_FCloseFile_t(0x462000);
FS_WriteFile_t FS_WriteFile = FS_WriteFile_t(0x426450);
FS_Write_t FS_Write = FS_Write_t(0x4C06E0);
FS_Printf_t FS_Printf = FS_Printf_t(0x459320);
FS_Read_t FS_Read = FS_Read_t(0x4A04C0);
FS_Seek_t FS_Seek = FS_Seek_t(0x4A63D0);
FS_FTell_t FS_FTell = FS_FTell_t(0x4E6760);

View File

@ -302,6 +302,9 @@ namespace Game
typedef int(__cdecl * FS_Write_t)(const void* buffer, size_t size, int file);
extern FS_Write_t FS_Write;
typedef int(__cdecl * FS_Printf_t)(int file, const char* fmt, ...);
extern FS_Printf_t FS_Printf;
typedef int(__cdecl * FS_Read_t)(void* buffer, size_t size, int file);
extern FS_Read_t FS_Read;