idac, idz, swdc: Fixed DInput brake/accel, added cubic steering

This commit is contained in:
Dniel97 2023-08-15 17:20:27 +02:00
parent 28ef2d719a
commit 3dc2ec6e69
Signed by untrusted user: Dniel97
GPG Key ID: 6180B3C768FB2E08
17 changed files with 257 additions and 84 deletions

View File

@ -65,6 +65,18 @@ path=
; Leave empty if you want to use Segatools built-in gamepad/wheel input. ; Leave empty if you want to use Segatools built-in gamepad/wheel input.
path= 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] [io4]
; Test button virtual-key code. Default is the 1 key. ; Test button virtual-key code. Default is the 1 key.
test=0x31 test=0x31
@ -72,6 +84,7 @@ test=0x31
service=0x32 service=0x32
; Keyboard button to increment coin counter. Default is the 3 key. ; Keyboard button to increment coin counter. Default is the 3 key.
coin=0x33 coin=0x33
; Input API selection for IO4 input emulator. ; Input API selection for IO4 input emulator.
; Set "xinput" to use a gamepad and "dinput" to use a steering wheel. ; Set "xinput" to use a gamepad and "dinput" to use a steering wheel.
mode=xinput mode=xinput
@ -79,20 +92,22 @@ mode=xinput
; pressed (e.g. when navigating menus between races). ; pressed (e.g. when navigating menus between races).
autoNeutral=1 autoNeutral=1
; Use the left thumbstick for steering instead of both on XInput Controllers. ; 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 singleStickSteering=1
; Use linear steering instead of the default non-linear cubing steering.
linearSteering=0
; Adjust scaling for steering wheel input. ; Adjust scaling for steering wheel input.
; ;
; This setting scales the steering wheel input so that the maximum positive ; This setting scales the steering wheel input so that the maximum positive
; and minimum negative steering inputs reported in the operator menu's input ; 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, ; 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 ; NOTE: This is not the same thing as DirectInput steering wheel movement
; range! Segatools cannot control the maximum angle of your physical steering ; range! Segatools cannot control the maximum angle of your physical steering
; wheel controller, this setting is vendor-specific and can only be adjusted ; wheel controller, this setting is vendor-specific and can only be adjusted
; in the Control Panel. ; in the Control Panel.
restrict=97 restrict=128
[dinput] [dinput]
; Name of the DirectInput wheel to use (or any text that occurs in its name) ; Name of the DirectInput wheel to use (or any text that occurs in its name)

View File

@ -8,6 +8,11 @@ option=
; NOTE: This has nothing to do with Windows %APPDATA%. ; NOTE: This has nothing to do with Windows %APPDATA%.
appdata= appdata=
[aime]
; Controls emulation of the Aime card reader assembly.
enable=1
aimePath=DEVICE\aime.txt
[dns] [dns]
; Insert the hostname or IP address of the server you wish to use here. ; 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. ; 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. ; Leave empty if you want to use Segatools built-in gamepad/wheel input.
path= 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] [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. ; Input API selection for JVS input emulator.
; Set "xinput" to use a gamepad and "dinput" to use a steering wheel. ; Set "xinput" to use a gamepad and "dinput" to use a steering wheel.
mode=xinput mode=xinput
@ -58,8 +82,10 @@ mode=xinput
; pressed (e.g. when navigating menus between races). ; pressed (e.g. when navigating menus between races).
autoNeutral=1 autoNeutral=1
; Use the left thumbstick for steering instead of both on XInput Controllers. ; 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=0 singleStickSteering=1
; Use linear steering instead of the default non-linear cubing steering.
linearSteering=0
; Adjust scaling for steering wheel input. ; Adjust scaling for steering wheel input.
; ;
; This setting scales the steering wheel input so that the maximum positive ; This setting scales the steering wheel input so that the maximum positive

View File

@ -40,6 +40,18 @@ path=
; Leave empty if you want to use Segatools built-in gamepad/wheel input. ; Leave empty if you want to use Segatools built-in gamepad/wheel input.
path= 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] [io4]
; Test button virtual-key code. Default is the 1 key. ; Test button virtual-key code. Default is the 1 key.
test=0x31 test=0x31
@ -49,22 +61,25 @@ service=0x32
coin=0x33 coin=0x33
; Input API selection for IO4 input emulator. ; Input API selection for IO4 input emulator.
; Set "xinput" to use a gamepad and "dinput" to use a steering wheel. ; Set "xinput" to use a gamepad and "dinput" to use a steering wheel.
mode=xinput mode=xinput
; Use the left thumbstick for steering instead of both on XInput Controllers. ; 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 singleStickSteering=1
; Use linear steering instead of the default non-linear cubing steering.
linearSteering=0
; Adjust scaling for steering wheel input. ; Adjust scaling for steering wheel input.
; ;
; This setting scales the steering wheel input so that the maximum positive ; This setting scales the steering wheel input so that the maximum positive
; and minimum negative steering inputs reported in the operator menu's input ; 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, ; 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 ; NOTE: This is not the same thing as DirectInput steering wheel movement
; range! Segatools cannot control the maximum angle of your physical steering ; range! Segatools cannot control the maximum angle of your physical steering
; wheel controller, this setting is vendor-specific and can only be adjusted ; wheel controller, this setting is vendor-specific and can only be adjusted
; in the Control Panel. ; in the Control Panel.
restrict=97 restrict=128
[dinput] [dinput]
; Name of the DirectInput wheel to use (or any text that occurs in its name) ; Name of the DirectInput wheel to use (or any text that occurs in its name)

View File

@ -75,7 +75,7 @@ static DWORD CALLBACK idac_pre_startup(void)
spike_hook_init(L".\\segatools.ini"); spike_hook_init(L".\\segatools.ini");
dprintf("--- End idac_pre_startup ---\n"); dprintf("--- End idac_pre_startup ---\n");
/* Jump to EXE start address */ /* Jump to EXE start address */

View File

@ -81,6 +81,12 @@ void idac_xi_config_load(struct idac_xi_config *cfg, const wchar_t *filename)
L"singleStickSteering", L"singleStickSteering",
0, 0,
filename); 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) void idac_io_config_load(struct idac_io_config *cfg, const wchar_t *filename)

View File

@ -24,6 +24,7 @@ struct idac_di_config {
struct idac_xi_config { struct idac_xi_config {
bool single_stick_steering; bool single_stick_steering;
bool linear_steering;
}; };
struct idac_io_config { struct idac_io_config {

View File

@ -262,8 +262,8 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg)
dprintf("Wheel: --- Begin configuration ---\n"); dprintf("Wheel: --- Begin configuration ---\n");
dprintf("Wheel: Device name . . . . : Contains \"%S\"\n", dprintf("Wheel: Device name . . . . : Contains \"%S\"\n",
cfg->device_name); cfg->device_name);
dprintf("Wheel: Brake axis . . . . : %S\n", accel_axis->name); dprintf("Wheel: Brake axis . . . . : %S\n", brake_axis->name);
dprintf("Wheel: Accelerator 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: Start button . . . : %i\n", cfg->start);
dprintf("Wheel: View Change button : %i\n", cfg->view_chg); dprintf("Wheel: View Change button : %i\n", cfg->view_chg);
dprintf("Wheel: Shift Down button . : %i\n", cfg->shift_dn); 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"); dprintf("Shifter: --- End configuration ---\n");
} }
idac_di_off_brake = accel_axis->off; idac_di_off_brake = brake_axis->off;
idac_di_off_accel = brake_axis->off; idac_di_off_accel = accel_axis->off;
idac_di_start = cfg->start; idac_di_start = cfg->start;
idac_di_view_chg = cfg->view_chg; idac_di_view_chg = cfg->view_chg;
idac_di_shift_dn = cfg->shift_dn; idac_di_shift_dn = cfg->shift_dn;

View File

@ -1,16 +1,16 @@
#include <windows.h> #include "idacio/xi.h"
#include <xinput.h>
#include <assert.h> #include <assert.h>
#include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <windows.h>
#include <xinput.h>
#include "idacio/backend.h" #include "idacio/backend.h"
#include "idacio/config.h" #include "idacio/config.h"
#include "idacio/idacio.h" #include "idacio/idacio.h"
#include "idacio/shifter.h" #include "idacio/shifter.h"
#include "idacio/xi.h"
#include "util/dprintf.h" #include "util/dprintf.h"
static void idac_xi_get_gamebtns(uint8_t *gamebtn_out); 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 HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg);
static const struct idac_io_backend idac_xi_backend = { static const struct idac_io_backend idac_xi_backend = {
.get_gamebtns = idac_xi_get_gamebtns, .get_gamebtns = idac_xi_get_gamebtns,
.get_shifter = idac_xi_get_shifter, .get_shifter = idac_xi_get_shifter,
.get_analogs = idac_xi_get_analogs, .get_analogs = idac_xi_get_analogs,
}; };
static bool idac_xi_single_stick_steering; 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; HRESULT hr;
assert(cfg != NULL); assert(cfg != NULL);
assert(backend != 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; return S_OK;
} }
HRESULT idac_io_poll(void) HRESULT idac_io_poll(void) {
{
return S_OK; 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: --- Begin configuration ---\n");
dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering); 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"); dprintf("XInput: --- End configuration ---\n");
idac_xi_single_stick_steering = cfg->single_stick_steering; idac_xi_single_stick_steering = cfg->single_stick_steering;
idac_xi_linear_steering = cfg->linear_steering;
return S_OK; 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; uint8_t gamebtn;
XINPUT_STATE xi; XINPUT_STATE xi;
WORD xb; WORD xb;
@ -102,8 +101,7 @@ static void idac_xi_get_gamebtns(uint8_t *gamebtn_out)
*gamebtn_out = gamebtn; *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_dn;
bool shift_up; bool shift_up;
XINPUT_STATE xi; XINPUT_STATE xi;
@ -144,8 +142,32 @@ static void idac_xi_get_shifter(uint8_t *gear)
*gear = idac_shifter_current_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; XINPUT_STATE xi;
int left; int left;
int right; int right;
@ -156,27 +178,33 @@ static void idac_xi_get_analogs(struct idac_io_analog_state *out)
XInputGetState(0, &xi); XInputGetState(0, &xi);
left = xi.Gamepad.sThumbLX; 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; right = xi.Gamepad.sThumbRX;
if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { if (!idac_xi_linear_steering) {
right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; // Apply non-linear transform for both sticks
} else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { left = apply_non_linear_transform(left, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; right = apply_non_linear_transform(right, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
} else { } 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) { if (idac_xi_single_stick_steering) {
out->wheel = left; out->wheel = left;
// dprintf("XInput: Single Stick Steering: %i\n", out->wheel);
} else { } else {
out->wheel = (left + right) / 2; out->wheel = (left + right) / 2;
} }

View File

@ -107,7 +107,7 @@ static DWORD CALLBACK idz_pre_startup(void)
spike_hook_init(L".\\segatools.ini"); spike_hook_init(L".\\segatools.ini");
dprintf("--- End idz_pre_startup ---\n"); dprintf("--- End idz_pre_startup ---\n");
/* Jump to EXE start address */ /* Jump to EXE start address */

View File

@ -81,6 +81,12 @@ void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename)
L"singleStickSteering", L"singleStickSteering",
0, 0,
filename); 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) void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename)

View File

@ -24,6 +24,7 @@ struct idz_di_config {
struct idz_xi_config { struct idz_xi_config {
bool single_stick_steering; bool single_stick_steering;
bool linear_steering;
}; };
struct idz_io_config { struct idz_io_config {

View File

@ -262,8 +262,8 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg)
dprintf("Wheel: --- Begin configuration ---\n"); dprintf("Wheel: --- Begin configuration ---\n");
dprintf("Wheel: Device name . . . . : Contains \"%S\"\n", dprintf("Wheel: Device name . . . . : Contains \"%S\"\n",
cfg->device_name); cfg->device_name);
dprintf("Wheel: Brake axis . . . . : %S\n", accel_axis->name); dprintf("Wheel: Brake axis . . . . : %S\n", brake_axis->name);
dprintf("Wheel: Accelerator 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: Start button . . . : %i\n", cfg->start);
dprintf("Wheel: View Change button : %i\n", cfg->view_chg); dprintf("Wheel: View Change button : %i\n", cfg->view_chg);
dprintf("Wheel: Shift Down button . : %i\n", cfg->shift_dn); 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"); dprintf("Shifter: --- End configuration ---\n");
} }
idz_di_off_brake = accel_axis->off; idz_di_off_brake = brake_axis->off;
idz_di_off_accel = brake_axis->off; idz_di_off_accel = accel_axis->off;
idz_di_start = cfg->start; idz_di_start = cfg->start;
idz_di_view_chg = cfg->view_chg; idz_di_view_chg = cfg->view_chg;
idz_di_shift_dn = cfg->shift_dn; idz_di_shift_dn = cfg->shift_dn;

View File

@ -1,6 +1,7 @@
#include <windows.h> #include <windows.h>
#include <xinput.h> #include <xinput.h>
#include <math.h>
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -26,6 +27,7 @@ static const struct idz_io_backend idz_xi_backend = {
}; };
static bool idz_xi_single_stick_steering; 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) 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: --- Begin configuration ---\n");
dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering); 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"); dprintf("XInput: --- End configuration ---\n");
idz_xi_single_stick_steering = cfg->single_stick_steering; idz_xi_single_stick_steering = cfg->single_stick_steering;
idz_xi_linear_steering = cfg->linear_steering;
return S_OK; return S_OK;
} }
@ -123,6 +127,31 @@ static void idz_xi_jvs_read_shifter(uint8_t *gear)
*gear = idz_shifter_current_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) static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out)
{ {
XINPUT_STATE xi; XINPUT_STATE xi;
@ -135,23 +164,28 @@ static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out)
XInputGetState(0, &xi); XInputGetState(0, &xi);
left = xi.Gamepad.sThumbLX; 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; right = xi.Gamepad.sThumbRX;
if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { if (!idz_xi_linear_steering) {
right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; // Apply non-linear transform for both sticks
} else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { left = apply_non_linear_transform(left, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; right = apply_non_linear_transform(right, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
} else { } 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) { if(idz_xi_single_stick_steering) {

View File

@ -71,6 +71,12 @@ void swdc_xi_config_load(struct swdc_xi_config *cfg, const wchar_t *filename)
L"singleStickSteering", L"singleStickSteering",
0, 0,
filename); 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) void swdc_io_config_load(struct swdc_io_config *cfg, const wchar_t *filename)

View File

@ -26,6 +26,7 @@ struct swdc_di_config {
struct swdc_xi_config { struct swdc_xi_config {
bool single_stick_steering; bool single_stick_steering;
bool linear_steering;
}; };
struct swdc_io_config { struct swdc_io_config {

View File

@ -246,8 +246,8 @@ static HRESULT swdc_di_config_apply(const struct swdc_di_config *cfg)
dprintf("Wheel: --- Begin configuration ---\n"); dprintf("Wheel: --- Begin configuration ---\n");
dprintf("Wheel: Device name . . . . : Contains \"%S\"\n", dprintf("Wheel: Device name . . . . : Contains \"%S\"\n",
cfg->device_name); cfg->device_name);
dprintf("Wheel: Brake axis . . . . . . : %S\n", accel_axis->name); dprintf("Wheel: Brake axis . . . . . . : %S\n", brake_axis->name);
dprintf("Wheel: Accelerator 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: Start button . . . . . : %i\n", cfg->start);
dprintf("Wheel: View Change button . . : %i\n", cfg->view_chg); dprintf("Wheel: View Change button . . : %i\n", cfg->view_chg);
dprintf("Wheel: Shift Down button . . : %i\n", cfg->shift_dn); 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: Reverse Accel Axis . . : %i\n", cfg->reverse_accel_axis);
dprintf("Wheel: --- End configuration ---\n"); dprintf("Wheel: --- End configuration ---\n");
swdc_di_off_brake = accel_axis->off; swdc_di_off_brake = brake_axis->off;
swdc_di_off_accel = brake_axis->off; swdc_di_off_accel = accel_axis->off;
swdc_di_start = cfg->start; swdc_di_start = cfg->start;
swdc_di_view_chg = cfg->view_chg; swdc_di_view_chg = cfg->view_chg;
swdc_di_shift_dn = cfg->shift_dn; swdc_di_shift_dn = cfg->shift_dn;

View File

@ -1,6 +1,7 @@
#include <windows.h> #include <windows.h>
#include <xinput.h> #include <xinput.h>
#include <math.h>
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -23,6 +24,7 @@ static const struct swdc_io_backend swdc_xi_backend = {
}; };
static bool swdc_xi_single_stick_steering; 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) 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: --- Begin configuration ---\n");
dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering); 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"); dprintf("XInput: --- End configuration ---\n");
swdc_xi_single_stick_steering = cfg->single_stick_steering; swdc_xi_single_stick_steering = cfg->single_stick_steering;
swdc_xi_linear_steering = cfg->linear_steering;
return S_OK; return S_OK;
} }
@ -123,6 +127,31 @@ static void swdc_xi_get_gamebtns(uint16_t *gamebtn_out)
*gamebtn_out = gamebtn; *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) static void swdc_xi_get_analogs(struct swdc_io_analog_state *out)
{ {
XINPUT_STATE xi; XINPUT_STATE xi;
@ -135,23 +164,28 @@ static void swdc_xi_get_analogs(struct swdc_io_analog_state *out)
XInputGetState(0, &xi); XInputGetState(0, &xi);
left = xi.Gamepad.sThumbLX; 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; right = xi.Gamepad.sThumbRX;
if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { if (!swdc_xi_linear_steering) {
right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; // Apply non-linear transform for both sticks
} else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { left = apply_non_linear_transform(left, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; right = apply_non_linear_transform(right, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
} else { } 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) { if (swdc_xi_single_stick_steering) {