From 3dc2ec6e69da347a52a585f164ba475fd85f27f8 Mon Sep 17 00:00:00 2001 From: Dniel97 Date: Tue, 15 Aug 2023 17:20:27 +0200 Subject: [PATCH] idac, idz, swdc: Fixed DInput brake/accel, added cubic steering --- dist/idac/segatools.ini | 21 +++++++-- dist/idz/segatools.ini | 30 ++++++++++++- dist/swdc/segatools.ini | 21 +++++++-- idachook/dllmain.c | 2 +- idacio/config.c | 6 +++ idacio/config.h | 1 + idacio/di.c | 8 ++-- idacio/xi.c | 94 ++++++++++++++++++++++++++--------------- idzhook/dllmain.c | 2 +- idzio/config.c | 6 +++ idzio/config.h | 1 + idzio/di.c | 8 ++-- idzio/xi.c | 64 +++++++++++++++++++++------- swdcio/config.c | 6 +++ swdcio/config.h | 1 + swdcio/di.c | 8 ++-- swdcio/xi.c | 62 +++++++++++++++++++++------ 17 files changed, 257 insertions(+), 84 deletions(-) diff --git a/dist/idac/segatools.ini b/dist/idac/segatools.ini index feae701..ce5fc8a 100644 --- a/dist/idac/segatools.ini +++ b/dist/idac/segatools.ini @@ -65,6 +65,18 @@ path= ; Leave empty if you want to use Segatools built-in gamepad/wheel input. path= +; ----------------------------------------------------------------------------- +; Input settings +; ----------------------------------------------------------------------------- + +; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal +; (not prefixed with 0x) virtual-key codes, a list of which can be found here: +; +; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +; +; This is, admittedly, not the most user-friendly configuration method in the +; world. An improved solution will be provided later. + [io4] ; Test button virtual-key code. Default is the 1 key. test=0x31 @@ -72,6 +84,7 @@ test=0x31 service=0x32 ; Keyboard button to increment coin counter. Default is the 3 key. coin=0x33 + ; Input API selection for IO4 input emulator. ; Set "xinput" to use a gamepad and "dinput" to use a steering wheel. mode=xinput @@ -79,20 +92,22 @@ mode=xinput ; pressed (e.g. when navigating menus between races). autoNeutral=1 ; Use the left thumbstick for steering instead of both on XInput Controllers. -; Not recommended as it will not give you the precision needed for this game +; Not recommended as it will not give you the precision needed for this game. singleStickSteering=1 +; Use linear steering instead of the default non-linear cubing steering. +linearSteering=0 ; Adjust scaling for steering wheel input. ; ; This setting scales the steering wheel input so that the maximum positive ; and minimum negative steering inputs reported in the operator menu's input ; test screen do not exceed the value below. The maximum possible value is 128, -; and the value that matches the input range of a real cabinet is 97. +; and the value that matches the input range of a real cabinet is 128. ; ; NOTE: This is not the same thing as DirectInput steering wheel movement ; range! Segatools cannot control the maximum angle of your physical steering ; wheel controller, this setting is vendor-specific and can only be adjusted ; in the Control Panel. -restrict=97 +restrict=128 [dinput] ; Name of the DirectInput wheel to use (or any text that occurs in its name) diff --git a/dist/idz/segatools.ini b/dist/idz/segatools.ini index 367d27e..6c68321 100644 --- a/dist/idz/segatools.ini +++ b/dist/idz/segatools.ini @@ -8,6 +8,11 @@ option= ; NOTE: This has nothing to do with Windows %APPDATA%. appdata= +[aime] +; Controls emulation of the Aime card reader assembly. +enable=1 +aimePath=DEVICE\aime.txt + [dns] ; Insert the hostname or IP address of the server you wish to use here. ; Note that 127.0.0.1, localhost etc are specifically rejected. @@ -50,7 +55,26 @@ path= ; Leave empty if you want to use Segatools built-in gamepad/wheel input. path= +; ----------------------------------------------------------------------------- +; Input settings +; ----------------------------------------------------------------------------- + +; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal +; (not prefixed with 0x) virtual-key codes, a list of which can be found here: +; +; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +; +; This is, admittedly, not the most user-friendly configuration method in the +; world. An improved solution will be provided later. + [io3] +; Test button virtual-key code. Default is the 1 key. +test=0x31 +; Service button virtual-key code. Default is the 2 key. +service=0x32 +; Keyboard button to increment coin counter. Default is the 3 key. +coin=0x33 + ; Input API selection for JVS input emulator. ; Set "xinput" to use a gamepad and "dinput" to use a steering wheel. mode=xinput @@ -58,8 +82,10 @@ mode=xinput ; pressed (e.g. when navigating menus between races). autoNeutral=1 ; Use the left thumbstick for steering instead of both on XInput Controllers. -; Not recommended as it will not give you the precision needed for this game -singleStickSteering=0 +; Not recommended as it will not give you the precision needed for this game. +singleStickSteering=1 +; Use linear steering instead of the default non-linear cubing steering. +linearSteering=0 ; Adjust scaling for steering wheel input. ; ; This setting scales the steering wheel input so that the maximum positive diff --git a/dist/swdc/segatools.ini b/dist/swdc/segatools.ini index dc0c003..ef762c5 100644 --- a/dist/swdc/segatools.ini +++ b/dist/swdc/segatools.ini @@ -40,6 +40,18 @@ path= ; Leave empty if you want to use Segatools built-in gamepad/wheel input. path= +; ----------------------------------------------------------------------------- +; Input settings +; ----------------------------------------------------------------------------- + +; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal +; (not prefixed with 0x) virtual-key codes, a list of which can be found here: +; +; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +; +; This is, admittedly, not the most user-friendly configuration method in the +; world. An improved solution will be provided later. + [io4] ; Test button virtual-key code. Default is the 1 key. test=0x31 @@ -49,22 +61,25 @@ service=0x32 coin=0x33 ; Input API selection for IO4 input emulator. ; Set "xinput" to use a gamepad and "dinput" to use a steering wheel. + mode=xinput ; Use the left thumbstick for steering instead of both on XInput Controllers. -; Not recommended as it will not give you the precision needed for this game +; Not recommended as it will not give you the precision needed for this game. singleStickSteering=1 +; Use linear steering instead of the default non-linear cubing steering. +linearSteering=0 ; Adjust scaling for steering wheel input. ; ; This setting scales the steering wheel input so that the maximum positive ; and minimum negative steering inputs reported in the operator menu's input ; test screen do not exceed the value below. The maximum possible value is 128, -; and the value that matches the input range of a real cabinet is 97. +; and the value that matches the input range of a real cabinet is 128. ; ; NOTE: This is not the same thing as DirectInput steering wheel movement ; range! Segatools cannot control the maximum angle of your physical steering ; wheel controller, this setting is vendor-specific and can only be adjusted ; in the Control Panel. -restrict=97 +restrict=128 [dinput] ; Name of the DirectInput wheel to use (or any text that occurs in its name) diff --git a/idachook/dllmain.c b/idachook/dllmain.c index fd3d447..5d6f695 100644 --- a/idachook/dllmain.c +++ b/idachook/dllmain.c @@ -75,7 +75,7 @@ static DWORD CALLBACK idac_pre_startup(void) spike_hook_init(L".\\segatools.ini"); - dprintf("--- End idac_pre_startup ---\n"); + dprintf("--- End idac_pre_startup ---\n"); /* Jump to EXE start address */ diff --git a/idacio/config.c b/idacio/config.c index 700f18b..ce7e201 100644 --- a/idacio/config.c +++ b/idacio/config.c @@ -81,6 +81,12 @@ void idac_xi_config_load(struct idac_xi_config *cfg, const wchar_t *filename) L"singleStickSteering", 0, filename); + + cfg->linear_steering = GetPrivateProfileIntW( + L"io4", + L"linearSteering", + 0, + filename); } void idac_io_config_load(struct idac_io_config *cfg, const wchar_t *filename) diff --git a/idacio/config.h b/idacio/config.h index 6e5563d..05332da 100644 --- a/idacio/config.h +++ b/idacio/config.h @@ -24,6 +24,7 @@ struct idac_di_config { struct idac_xi_config { bool single_stick_steering; + bool linear_steering; }; struct idac_io_config { diff --git a/idacio/di.c b/idacio/di.c index 0d741cd..2494082 100644 --- a/idacio/di.c +++ b/idacio/di.c @@ -262,8 +262,8 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg) dprintf("Wheel: --- Begin configuration ---\n"); dprintf("Wheel: Device name . . . . : Contains \"%S\"\n", cfg->device_name); - dprintf("Wheel: Brake axis . . . . : %S\n", accel_axis->name); - dprintf("Wheel: Accelerator axis . : %S\n", brake_axis->name); + dprintf("Wheel: Brake axis . . . . : %S\n", brake_axis->name); + dprintf("Wheel: Accel axis . . . . : %S\n", accel_axis->name); dprintf("Wheel: Start button . . . : %i\n", cfg->start); dprintf("Wheel: View Change button : %i\n", cfg->view_chg); dprintf("Wheel: Shift Down button . : %i\n", cfg->shift_dn); @@ -286,8 +286,8 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg) dprintf("Shifter: --- End configuration ---\n"); } - idac_di_off_brake = accel_axis->off; - idac_di_off_accel = brake_axis->off; + idac_di_off_brake = brake_axis->off; + idac_di_off_accel = accel_axis->off; idac_di_start = cfg->start; idac_di_view_chg = cfg->view_chg; idac_di_shift_dn = cfg->shift_dn; diff --git a/idacio/xi.c b/idacio/xi.c index 87f3fbd..3a41aaa 100644 --- a/idacio/xi.c +++ b/idacio/xi.c @@ -1,16 +1,16 @@ -#include -#include +#include "idacio/xi.h" #include +#include #include #include +#include +#include #include "idacio/backend.h" #include "idacio/config.h" #include "idacio/idacio.h" #include "idacio/shifter.h" -#include "idacio/xi.h" - #include "util/dprintf.h" static void idac_xi_get_gamebtns(uint8_t *gamebtn_out); @@ -20,15 +20,15 @@ static void idac_xi_get_analogs(struct idac_io_analog_state *out); static HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg); static const struct idac_io_backend idac_xi_backend = { - .get_gamebtns = idac_xi_get_gamebtns, - .get_shifter = idac_xi_get_shifter, - .get_analogs = idac_xi_get_analogs, + .get_gamebtns = idac_xi_get_gamebtns, + .get_shifter = idac_xi_get_shifter, + .get_analogs = idac_xi_get_analogs, }; static bool idac_xi_single_stick_steering; +static bool idac_xi_linear_steering; -HRESULT idac_xi_init(const struct idac_xi_config *cfg, const struct idac_io_backend **backend) -{ +HRESULT idac_xi_init(const struct idac_xi_config *cfg, const struct idac_io_backend **backend) { HRESULT hr; assert(cfg != NULL); assert(backend != NULL); @@ -45,24 +45,23 @@ HRESULT idac_xi_init(const struct idac_xi_config *cfg, const struct idac_io_back return S_OK; } -HRESULT idac_io_poll(void) -{ +HRESULT idac_io_poll(void) { return S_OK; } -static HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg) -{ +static HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg) { dprintf("XInput: --- Begin configuration ---\n"); dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering); + dprintf("XInput: Linear Steering . . . : %i\n", cfg->linear_steering); dprintf("XInput: --- End configuration ---\n"); idac_xi_single_stick_steering = cfg->single_stick_steering; + idac_xi_linear_steering = cfg->linear_steering; return S_OK; } -static void idac_xi_get_gamebtns(uint8_t *gamebtn_out) -{ +static void idac_xi_get_gamebtns(uint8_t *gamebtn_out) { uint8_t gamebtn; XINPUT_STATE xi; WORD xb; @@ -102,8 +101,7 @@ static void idac_xi_get_gamebtns(uint8_t *gamebtn_out) *gamebtn_out = gamebtn; } -static void idac_xi_get_shifter(uint8_t *gear) -{ +static void idac_xi_get_shifter(uint8_t *gear) { bool shift_dn; bool shift_up; XINPUT_STATE xi; @@ -144,8 +142,32 @@ static void idac_xi_get_shifter(uint8_t *gear) *gear = idac_shifter_current_gear(); } -static void idac_xi_get_analogs(struct idac_io_analog_state *out) -{ +static int apply_non_linear_transform(int value, int deadzone_center) { + const int max_input = 32767; + const double power_factor = 3.0; + + // Apply deadzone only after passing the center threshold + if (abs(value) < deadzone_center) { + return 0; + } + + // Scale the value to the range [-1.0, 1.0] + double scaled_value = (abs(value) - deadzone_center) / (double)(max_input - deadzone_center); + + // Apply a non-linear transform (cubing in this case) and preserve the sign + double signed_value = copysign(pow(scaled_value, power_factor), value); + + // Scale the value back to the range [-32770, 32767] + int transformed_value = (int)(signed_value * max_input); + + // Clamp the value to the range [-32767, 32767] + transformed_value = (transformed_value > max_input) ? max_input : transformed_value; + transformed_value = (transformed_value < -max_input) ? -max_input : transformed_value; + + return transformed_value; +} + +static void idac_xi_get_analogs(struct idac_io_analog_state *out) { XINPUT_STATE xi; int left; int right; @@ -156,27 +178,33 @@ static void idac_xi_get_analogs(struct idac_io_analog_state *out) XInputGetState(0, &xi); left = xi.Gamepad.sThumbLX; - - if (left < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { - left += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; - } else if (left > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { - left -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; - } else { - left = 0; - } - right = xi.Gamepad.sThumbRX; - if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; - } else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + if (!idac_xi_linear_steering) { + // Apply non-linear transform for both sticks + left = apply_non_linear_transform(left, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + right = apply_non_linear_transform(right, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); } else { - right = 0; + if (left < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + left += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; + } else if (left > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + left -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; + } else { + left = 0; + } + + if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { + right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + } else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { + right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + } else { + right = 0; + } } if (idac_xi_single_stick_steering) { out->wheel = left; + // dprintf("XInput: Single Stick Steering: %i\n", out->wheel); } else { out->wheel = (left + right) / 2; } diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index 88fffbf..fe78b6f 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -107,7 +107,7 @@ static DWORD CALLBACK idz_pre_startup(void) spike_hook_init(L".\\segatools.ini"); - dprintf("--- End idz_pre_startup ---\n"); + dprintf("--- End idz_pre_startup ---\n"); /* Jump to EXE start address */ diff --git a/idzio/config.c b/idzio/config.c index d06476b..e49b550 100644 --- a/idzio/config.c +++ b/idzio/config.c @@ -81,6 +81,12 @@ void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename) L"singleStickSteering", 0, filename); + + cfg->linear_steering = GetPrivateProfileIntW( + L"io3", + L"linearSteering", + 0, + filename); } void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename) diff --git a/idzio/config.h b/idzio/config.h index 19b837a..3d2668b 100644 --- a/idzio/config.h +++ b/idzio/config.h @@ -24,6 +24,7 @@ struct idz_di_config { struct idz_xi_config { bool single_stick_steering; + bool linear_steering; }; struct idz_io_config { diff --git a/idzio/di.c b/idzio/di.c index c363650..c16be14 100644 --- a/idzio/di.c +++ b/idzio/di.c @@ -262,8 +262,8 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) dprintf("Wheel: --- Begin configuration ---\n"); dprintf("Wheel: Device name . . . . : Contains \"%S\"\n", cfg->device_name); - dprintf("Wheel: Brake axis . . . . : %S\n", accel_axis->name); - dprintf("Wheel: Accelerator axis . : %S\n", brake_axis->name); + dprintf("Wheel: Brake axis . . . . : %S\n", brake_axis->name); + dprintf("Wheel: Accel axis . . . . : %S\n", accel_axis->name); dprintf("Wheel: Start button . . . : %i\n", cfg->start); dprintf("Wheel: View Change button : %i\n", cfg->view_chg); dprintf("Wheel: Shift Down button . : %i\n", cfg->shift_dn); @@ -286,8 +286,8 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) dprintf("Shifter: --- End configuration ---\n"); } - idz_di_off_brake = accel_axis->off; - idz_di_off_accel = brake_axis->off; + idz_di_off_brake = brake_axis->off; + idz_di_off_accel = accel_axis->off; idz_di_start = cfg->start; idz_di_view_chg = cfg->view_chg; idz_di_shift_dn = cfg->shift_dn; diff --git a/idzio/xi.c b/idzio/xi.c index c3d33d6..ebd1ea9 100644 --- a/idzio/xi.c +++ b/idzio/xi.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ static const struct idz_io_backend idz_xi_backend = { }; static bool idz_xi_single_stick_steering; +static bool idz_xi_linear_steering; HRESULT idz_xi_init(const struct idz_xi_config *cfg, const struct idz_io_backend **backend) { @@ -49,9 +51,11 @@ static HRESULT idz_xi_config_apply(const struct idz_xi_config *cfg) { dprintf("XInput: --- Begin configuration ---\n"); dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering); + dprintf("XInput: Linear Steering . . . : %i\n", cfg->linear_steering); dprintf("XInput: --- End configuration ---\n"); idz_xi_single_stick_steering = cfg->single_stick_steering; + idz_xi_linear_steering = cfg->linear_steering; return S_OK; } @@ -123,6 +127,31 @@ static void idz_xi_jvs_read_shifter(uint8_t *gear) *gear = idz_shifter_current_gear(); } +static int apply_non_linear_transform(int value, int deadzone_center) { + const int max_input = 32767; + const double power_factor = 3.0; + + // Apply deadzone only after passing the center threshold + if (abs(value) < deadzone_center) { + return 0; + } + + // Scale the value to the range [-1.0, 1.0] + double scaled_value = (abs(value) - deadzone_center) / (double)(max_input - deadzone_center); + + // Apply a non-linear transform (cubing in this case) and preserve the sign + double signed_value = copysign(pow(scaled_value, power_factor), value); + + // Scale the value back to the range [-32770, 32767] + int transformed_value = (int)(signed_value * max_input); + + // Clamp the value to the range [-32767, 32767] + transformed_value = (transformed_value > max_input) ? max_input : transformed_value; + transformed_value = (transformed_value < -max_input) ? -max_input : transformed_value; + + return transformed_value; +} + static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out) { XINPUT_STATE xi; @@ -135,23 +164,28 @@ static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out) XInputGetState(0, &xi); left = xi.Gamepad.sThumbLX; - - if (left < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { - left += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; - } else if (left > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { - left -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; - } else { - left = 0; - } - right = xi.Gamepad.sThumbRX; - - if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; - } else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + + if (!idz_xi_linear_steering) { + // Apply non-linear transform for both sticks + left = apply_non_linear_transform(left, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + right = apply_non_linear_transform(right, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); } else { - right = 0; + if (left < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + left += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; + } else if (left > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + left -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; + } else { + left = 0; + } + + if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { + right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + } else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { + right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + } else { + right = 0; + } } if(idz_xi_single_stick_steering) { diff --git a/swdcio/config.c b/swdcio/config.c index 7396338..ae4a1f5 100644 --- a/swdcio/config.c +++ b/swdcio/config.c @@ -71,6 +71,12 @@ void swdc_xi_config_load(struct swdc_xi_config *cfg, const wchar_t *filename) L"singleStickSteering", 0, filename); + + cfg->linear_steering = GetPrivateProfileIntW( + L"io4", + L"linearSteering", + 0, + filename); } void swdc_io_config_load(struct swdc_io_config *cfg, const wchar_t *filename) diff --git a/swdcio/config.h b/swdcio/config.h index 4fa482f..0225d59 100644 --- a/swdcio/config.h +++ b/swdcio/config.h @@ -26,6 +26,7 @@ struct swdc_di_config { struct swdc_xi_config { bool single_stick_steering; + bool linear_steering; }; struct swdc_io_config { diff --git a/swdcio/di.c b/swdcio/di.c index dd3d737..0fd0118 100644 --- a/swdcio/di.c +++ b/swdcio/di.c @@ -246,8 +246,8 @@ static HRESULT swdc_di_config_apply(const struct swdc_di_config *cfg) dprintf("Wheel: --- Begin configuration ---\n"); dprintf("Wheel: Device name . . . . : Contains \"%S\"\n", cfg->device_name); - dprintf("Wheel: Brake axis . . . . . . : %S\n", accel_axis->name); - dprintf("Wheel: Accelerator axis . . . : %S\n", brake_axis->name); + dprintf("Wheel: Brake axis . . . . . . : %S\n", brake_axis->name); + dprintf("Wheel: Accel axis . . . . . . : %S\n", accel_axis->name); dprintf("Wheel: Start button . . . . . : %i\n", cfg->start); dprintf("Wheel: View Change button . . : %i\n", cfg->view_chg); dprintf("Wheel: Shift Down button . . : %i\n", cfg->shift_dn); @@ -260,8 +260,8 @@ static HRESULT swdc_di_config_apply(const struct swdc_di_config *cfg) dprintf("Wheel: Reverse Accel Axis . . : %i\n", cfg->reverse_accel_axis); dprintf("Wheel: --- End configuration ---\n"); - swdc_di_off_brake = accel_axis->off; - swdc_di_off_accel = brake_axis->off; + swdc_di_off_brake = brake_axis->off; + swdc_di_off_accel = accel_axis->off; swdc_di_start = cfg->start; swdc_di_view_chg = cfg->view_chg; swdc_di_shift_dn = cfg->shift_dn; diff --git a/swdcio/xi.c b/swdcio/xi.c index 9be56a2..9a98ec4 100644 --- a/swdcio/xi.c +++ b/swdcio/xi.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ static const struct swdc_io_backend swdc_xi_backend = { }; static bool swdc_xi_single_stick_steering; +static bool swdc_xi_linear_steering; HRESULT swdc_xi_init(const struct swdc_xi_config *cfg, const struct swdc_io_backend **backend) { @@ -51,9 +53,11 @@ static HRESULT swdc_xi_config_apply(const struct swdc_xi_config *cfg) { dprintf("XInput: --- Begin configuration ---\n"); dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering); + dprintf("XInput: Linear Steering . . . : %i\n", cfg->linear_steering); dprintf("XInput: --- End configuration ---\n"); swdc_xi_single_stick_steering = cfg->single_stick_steering; + swdc_xi_linear_steering = cfg->linear_steering; return S_OK; } @@ -123,6 +127,31 @@ static void swdc_xi_get_gamebtns(uint16_t *gamebtn_out) *gamebtn_out = gamebtn; } +static int apply_non_linear_transform(int value, int deadzone_center) { + const int max_input = 32767; + const double power_factor = 3.0; + + // Apply deadzone only after passing the center threshold + if (abs(value) < deadzone_center) { + return 0; + } + + // Scale the value to the range [-1.0, 1.0] + double scaled_value = (abs(value) - deadzone_center) / (double)(max_input - deadzone_center); + + // Apply a non-linear transform (cubing in this case) and preserve the sign + double signed_value = copysign(pow(scaled_value, power_factor), value); + + // Scale the value back to the range [-32770, 32767] + int transformed_value = (int)(signed_value * max_input); + + // Clamp the value to the range [-32767, 32767] + transformed_value = (transformed_value > max_input) ? max_input : transformed_value; + transformed_value = (transformed_value < -max_input) ? -max_input : transformed_value; + + return transformed_value; +} + static void swdc_xi_get_analogs(struct swdc_io_analog_state *out) { XINPUT_STATE xi; @@ -135,23 +164,28 @@ static void swdc_xi_get_analogs(struct swdc_io_analog_state *out) XInputGetState(0, &xi); left = xi.Gamepad.sThumbLX; - - if (left < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { - left += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; - } else if (left > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { - left -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; - } else { - left = 0; - } - right = xi.Gamepad.sThumbRX; - if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; - } else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + if (!swdc_xi_linear_steering) { + // Apply non-linear transform for both sticks + left = apply_non_linear_transform(left, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + right = apply_non_linear_transform(right, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); } else { - right = 0; + if (left < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + left += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; + } else if (left > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + left -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; + } else { + left = 0; + } + + if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { + right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + } else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { + right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; + } else { + right = 0; + } } if (swdc_xi_single_stick_steering) {