Compare commits

..

4 Commits

27 changed files with 220 additions and 73 deletions

View File

@ -18,6 +18,7 @@ struct aime_dll {
struct aime_dll_config { struct aime_dll_config {
wchar_t path[MAX_PATH]; wchar_t path[MAX_PATH];
bool path64;
}; };
extern struct aime_dll aime_dll; extern struct aime_dll aime_dll;

View File

@ -9,18 +9,59 @@
#include "board/config.h" #include "board/config.h"
#include "board/sg-reader.h" #include "board/sg-reader.h"
#include "util/dprintf.h"
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename) static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
{ {
assert(cfg != NULL); assert(cfg != NULL);
assert(filename != NULL); assert(filename != NULL);
GetPrivateProfileStringW( // Workaround for x64/x86 external IO dlls
// path32 for 32bit, path64 for 64bit
// for else.. is that possible? idk
if (cfg->path64) {
#if defined(ENV32BIT)
// Always empty, due to amdaemon being 64 bit in 32 bit mode
memset(cfg->path, 0, sizeof(cfg->path));
#elif defined(ENV64BIT)
GetPrivateProfileStringW(
L"aimeio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
#else
#error "Unknown environment"
#endif
} else {
GetPrivateProfileStringW(
L"aimeio", L"aimeio",
L"path", L"path",
L"", L"",
cfg->path, cfg->path,
_countof(cfg->path), _countof(cfg->path),
filename); filename);
}
} }
void aime_config_load(struct aime_config *cfg, const wchar_t *filename) void aime_config_load(struct aime_config *cfg, const wchar_t *filename)

View File

@ -38,7 +38,7 @@ void chuni_io_jvs_read_coin_counter(uint16_t *out)
return; return;
} }
if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) { if (GetAsyncKeyState(chuni_io_cfg.vk_coin) & 0x8000) {
if (!chuni_io_coin) { if (!chuni_io_coin) {
chuni_io_coin = true; chuni_io_coin = true;
chuni_io_coins++; chuni_io_coins++;
@ -62,15 +62,6 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
*opbtn |= CHUNI_IO_OPBTN_SERVICE; *opbtn |= CHUNI_IO_OPBTN_SERVICE;
} }
if (GetAsyncKeyState(chuni_io_cfg.vk_coin) & 0x8000) {
if (!chuni_io_coin) {
chuni_io_coin = true;
*opbtn |= CHUNI_IO_OPBTN_COIN;
}
} else {
chuni_io_coin = false;
}
if (chuni_io_cfg.vk_ir_emu) { if (chuni_io_cfg.vk_ir_emu) {
// Use emulated AIR // Use emulated AIR
if (GetAsyncKeyState(chuni_io_cfg.vk_ir_emu)) { if (GetAsyncKeyState(chuni_io_cfg.vk_ir_emu)) {

View File

@ -57,7 +57,7 @@ void chuni_io_config_load(
filename); filename);
} }
GetPrivateProfileStringW(L"slider", L"ledport", L"COM2", port_input, 6, filename); GetPrivateProfileStringW(L"slider", L"ledport", L"COM5", port_input, 6, filename);
wcsncpy(cfg->led_com, L"\\\\.\\", 4); wcsncpy(cfg->led_com, L"\\\\.\\", 4);
wcsncat_s(cfg->led_com, 11, port_input, 6); wcsncat_s(cfg->led_com, 11, port_input, 6);
} }

View File

@ -141,6 +141,9 @@ void chusan_hook_config_load(
memset(cfg, 0, sizeof(*cfg)); memset(cfg, 0, sizeof(*cfg));
// Force load the 64bit Aime DLL instead of the 32bit one
cfg->aime.dll.path64 = true;
platform_config_load(&cfg->platform, filename); platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename); aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename); dvd_config_load(&cfg->dvd, filename);

View File

@ -100,11 +100,7 @@ static DWORD CALLBACK chusan_pre_startup(void)
} }
bool *dipsw = &chusan_hook_cfg.platform.dipsw.dipsw[0]; bool *dipsw = &chusan_hook_cfg.platform.dipsw.dipsw[0];
bool *is_sp = dipsw + 2;
if (dipsw[1] != dipsw[2]) {
dprintf("DipSw: DipSw2 and 3 must be set to the same value!\n");
goto fail;
}
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
switch (i) { switch (i) {
@ -117,13 +113,21 @@ static DWORD CALLBACK chusan_pre_startup(void)
break; break;
case 2: case 2:
dprintf("DipSw: Aime Reader: %s\n", dipsw[2] ? "CVT" : "SP"); dprintf("DipSw: Cab Type: %s\n", is_sp ? "SP" : "CVT");
break; break;
} }
} }
unsigned int first_port = dipsw[1] ? 2 : 20; unsigned int first_port = is_sp ? 20 : 2;
if (is_sp) {
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
}
hr = led15093_hook_init(&chusan_hook_cfg.led15093, first_port, 2, 2, 1); hr = led15093_hook_init(&chusan_hook_cfg.led15093, first_port, 2, 2, 1);
@ -131,7 +135,7 @@ static DWORD CALLBACK chusan_pre_startup(void)
goto fail; goto fail;
} }
hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, dipsw[2] ? 2 : 3, chusan_hook_mod); hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_sp ? 3: 2, chusan_hook_mod);
if (FAILED(hr)) { if (FAILED(hr)) {
goto fail; goto fail;

View File

@ -73,6 +73,7 @@ static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state)
beams = 0; beams = 0;
chuni_dll.jvs_poll(&opbtn, &beams); chuni_dll.jvs_poll(&opbtn, &beams);
chuni_dll.jvs_read_coin_counter(&coins);
if (chuni_dll.api_version >= 0x0101) { if (chuni_dll.api_version >= 0x0101) {
// Use correct mapping // Use correct mapping
@ -90,9 +91,7 @@ static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state)
state->buttons[0] |= IO4_BUTTON_SERVICE; state->buttons[0] |= IO4_BUTTON_SERVICE;
} }
if (opbtn & CHUNI_IO_OPBTN_COIN) { // Update the coin counter with the value from jvs_read_coin_counter
coins++;
}
state->chutes[0] = coins << 8; state->chutes[0] = coins << 8;
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {

View File

@ -82,3 +82,7 @@ coin=0x33
;cell31=0x53 ;cell31=0x53
;cell30=0x53 ;cell30=0x53
; ... etc ... ; ... etc ...
; Enable slider LED serial output. This follows OpeNITHM Serial LED Protocol.
; eg. COM5
;ledport=

View File

@ -16,9 +16,9 @@ aimePath=DEVICE\aime.txt
;highbaud=1 ;highbaud=1
[aimeio] [aimeio]
; x64 aimeio dll path. ; Uncomment this if you have custom (x64) aime implementation.
; Uncomment this if you have custom aime implementation. ; Leave empty if you want to use Segatools built-in keyboard input.
;path64= ;path=
[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.
@ -35,7 +35,7 @@ enable=1
; The /24 LAN subnet that the emulated keychip will tell the game to expect. ; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and ; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168. ; that subnet must start with 192.168.
subnet=192.168.100.0 subnet=192.168.139.0
[gpio] [gpio]
; ALLS DIP switches. ; ALLS DIP switches.
@ -49,10 +49,10 @@ freeplay=0
; LAN Install: If multiple machines are present on the same LAN then set ; LAN Install: If multiple machines are present on the same LAN then set
; this to 1 on exactly one machine and set this to 0 on all others. ; this to 1 on exactly one machine and set this to 0 on all others.
dipsw1=1 dipsw1=1
; Monitor type: 0 = 120FPS (SP), 1 = 60FPS (CVT) ; Monitor type: 0 = 120FPS, 1 = 60FPS
dipsw2=1 dipsw2=1
; Aime reader hardware type: 0 = SP, 1 = CVT. Both dipsw2 and dipsw3 must be ; Cab type: 0 = SP, 1 = CVT. SP will enable VFD and eMoney. This setting will switch
; the same value. ; the LED 837-15093-06 COM port and the AiMe reder hardware generation as well.
dipsw3=1 dipsw3=1
[gfx] [gfx]

View File

@ -29,7 +29,7 @@ enable=1
; If you disable netenv then you must set this to your LAN's IP subnet, and ; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168. Set it to your LAN's subnet if you ; that subnet must start with 192.168. Set it to your LAN's subnet if you
; want to play head-to-head using netenv=1. ; want to play head-to-head using netenv=1.
subnet=192.168.100.0 subnet=192.168.158.0
; Override the keychip's region code. Most games seem to pay attention to the ; Override the keychip's region code. Most games seem to pay attention to the
; DS EEPROM region code and not the keychip region code, and this seems to be ; DS EEPROM region code and not the keychip region code, and this seems to be
@ -174,3 +174,8 @@ gear6=18
; (Needed when using DirectInput for the Dualshock 4 for example) ; (Needed when using DirectInput for the Dualshock 4 for example)
reverseAccelAxis=0 reverseAccelAxis=0
reverseBrakeAxis=0 reverseBrakeAxis=0
; Force feedback settings.
; Strength of the force feedback spring effect in percent. Possible values
; are 0-100.
centerSpringStrength=30

View File

@ -173,3 +173,8 @@ gear6=18
; (Needed when using DirectInput for the Dualshock 4 for example) ; (Needed when using DirectInput for the Dualshock 4 for example)
reverseAccelAxis=0 reverseAccelAxis=0
reverseBrakeAxis=0 reverseBrakeAxis=0
; Force feedback settings.
; Strength of the force feedback spring effect in percent. Possible values
; are 0-100.
centerSpringStrength=30

View File

@ -133,3 +133,8 @@ wheelGreen=10
; (Needed when using DirectInput for the Dualshock 4 for example) ; (Needed when using DirectInput for the Dualshock 4 for example)
reverseAccelAxis=0 reverseAccelAxis=0
reverseBrakeAxis=0 reverseBrakeAxis=0
; Force feedback settings.
; Strength of the force feedback spring effect in percent. Possible values
; are 0-100.
centerSpringStrength=30

View File

@ -79,6 +79,14 @@ void idac_di_config_load(struct idac_di_config *cfg, const wchar_t *filename)
cfg->gear[i] = GetPrivateProfileIntW(L"dinput", key, i + 1, filename); cfg->gear[i] = GetPrivateProfileIntW(L"dinput", key, i + 1, filename);
} }
// FFB configuration
cfg->center_spring_strength = GetPrivateProfileIntW(
L"dinput",
L"centerSpringStrength",
30,
filename);
} }
void idac_xi_config_load(struct idac_xi_config *cfg, const wchar_t *filename) void idac_xi_config_load(struct idac_xi_config *cfg, const wchar_t *filename)

View File

@ -23,6 +23,9 @@ struct idac_di_config {
uint8_t gear[6]; uint8_t gear[6];
bool reverse_brake_axis; bool reverse_brake_axis;
bool reverse_accel_axis; bool reverse_accel_axis;
// FFB configuration
uint16_t center_spring_strength;
}; };
struct idac_xi_config { struct idac_xi_config {

View File

@ -44,7 +44,8 @@ HRESULT idac_di_dev_start(IDirectInputDevice8W *dev, HWND wnd)
return hr; return hr;
} }
void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out) void idac_di_dev_start_fx(
IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength)
{ {
/* Set up force-feedback on devices that support it. This is just a stub /* Set up force-feedback on devices that support it. This is just a stub
for the time being, since we don't yet know how the serial port force for the time being, since we don't yet know how the serial port force
@ -67,7 +68,7 @@ void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
DWORD axis; DWORD axis;
LONG direction; LONG direction;
DIEFFECT fx; DIEFFECT fx;
DICONSTANTFORCE cf; DICONDITION cond;
HRESULT hr; HRESULT hr;
assert(dev != NULL); assert(dev != NULL);
@ -77,11 +78,17 @@ void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
dprintf("DirectInput: Starting force feedback (may take a sec)\n"); dprintf("DirectInput: Starting force feedback (may take a sec)\n");
// Auto-centering effect
axis = DIJOFS_X; axis = DIJOFS_X;
direction = 0; direction = 0;
memset(&cf, 0, sizeof(cf)); memset(&cond, 0, sizeof(cond));
cf.lMagnitude = 0; cond.lOffset = 0;
cond.lPositiveCoefficient = strength;
cond.lNegativeCoefficient = strength;
cond.dwPositiveSaturation = strength; // For FG920?
cond.dwNegativeSaturation = strength; // For FG920?
cond.lDeadBand = 0;
memset(&fx, 0, sizeof(fx)); memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx); fx.dwSize = sizeof(fx);
@ -93,20 +100,19 @@ void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
fx.cAxes = 1; fx.cAxes = 1;
fx.rgdwAxes = &axis; fx.rgdwAxes = &axis;
fx.rglDirection = &direction; fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cf); fx.cbTypeSpecificParams = sizeof(cond);
fx.lpvTypeSpecificParams = &cf; fx.lpvTypeSpecificParams = &cond;
hr = IDirectInputDevice8_CreateEffect( hr = IDirectInputDevice8_CreateEffect(
dev, dev,
&GUID_ConstantForce, &GUID_Spring,
&fx, &fx,
&obj, &obj,
NULL); NULL);
if (FAILED(hr)) { if (FAILED(hr)) {
dprintf("DirectInput: DirectInput force feedback unavailable: %08x\n", dprintf("DirectInput: Centering spring force feedback unavailable: %08x\n",
(int) hr); (int) hr);
return; return;
} }
@ -114,15 +120,15 @@ void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
if (FAILED(hr)) { if (FAILED(hr)) {
IDirectInputEffect_Release(obj); IDirectInputEffect_Release(obj);
dprintf("DirectInput: DirectInput force feedback start failed: %08x\n", dprintf("DirectInput: Centering spring force feedback start failed: %08x\n",
(int) hr); (int) hr);
return; return;
} }
*out = obj; *out = obj;
dprintf("DirectInput: Force feedback initialized and set to zero\n"); dprintf("DirectInput: Centering spring effects initialized with strength %d%%\n",
strength / 100);
} }
HRESULT idac_di_dev_poll( HRESULT idac_di_dev_poll(

View File

@ -11,7 +11,7 @@ union idac_di_state {
}; };
HRESULT idac_di_dev_start(IDirectInputDevice8W *dev, HWND wnd); HRESULT idac_di_dev_start(IDirectInputDevice8W *dev, HWND wnd);
void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out); void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength);
HRESULT idac_di_dev_poll( HRESULT idac_di_dev_poll(
IDirectInputDevice8W *dev, IDirectInputDevice8W *dev,
HWND wnd, HWND wnd,

View File

@ -75,6 +75,7 @@ static uint8_t idac_di_gear[6];
static bool idac_di_use_pedals; static bool idac_di_use_pedals;
static bool idac_di_reverse_brake_axis; static bool idac_di_reverse_brake_axis;
static bool idac_di_reverse_accel_axis; static bool idac_di_reverse_accel_axis;
static uint16_t idac_di_center_spring_strength;
HRESULT idac_di_init( HRESULT idac_di_init(
const struct idac_di_config *cfg, const struct idac_di_config *cfg,
@ -173,7 +174,9 @@ HRESULT idac_di_init(
return hr; return hr;
} }
idac_di_dev_start_fx(idac_di_dev, &idac_di_fx); // Convert the strength from 0-100 to 0-10000 for DirectInput
idac_di_dev_start_fx(idac_di_dev, &idac_di_fx,
idac_di_center_spring_strength * 100);
if (cfg->pedals_name[0] != L'\0') { if (cfg->pedals_name[0] != L'\0') {
hr = IDirectInput8_EnumDevices( hr = IDirectInput8_EnumDevices(
@ -364,6 +367,16 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg)
idac_di_gear[i] = cfg->gear[i]; idac_di_gear[i] = cfg->gear[i];
} }
// FFB configuration
if (cfg->center_spring_strength < 0 || cfg->center_spring_strength > 100) {
dprintf("Wheel: Invalid center spring strength: %i\n", cfg->center_spring_strength);
return E_INVALIDARG;
}
idac_di_center_spring_strength = cfg->center_spring_strength;
return S_OK; return S_OK;
} }

View File

@ -76,7 +76,14 @@ void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename)
swprintf_s(key, _countof(key), L"gear%i", i + 1); swprintf_s(key, _countof(key), L"gear%i", i + 1);
cfg->gear[i] = GetPrivateProfileIntW(L"dinput", key, i + 1, filename); cfg->gear[i] = GetPrivateProfileIntW(L"dinput", key, i + 1, filename);
} }
// FFB configuration
cfg->center_spring_strength = GetPrivateProfileIntW(
L"dinput",
L"centerSpringStrength",
30,
filename);
} }
void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename) void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename)

View File

@ -21,6 +21,9 @@ struct idz_di_config {
uint8_t gear[6]; uint8_t gear[6];
bool reverse_brake_axis; bool reverse_brake_axis;
bool reverse_accel_axis; bool reverse_accel_axis;
// FFB configuration
uint16_t center_spring_strength;
}; };
struct idz_xi_config { struct idz_xi_config {

View File

@ -44,7 +44,8 @@ HRESULT idz_di_dev_start(IDirectInputDevice8W *dev, HWND wnd)
return hr; return hr;
} }
void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out) void idz_di_dev_start_fx(
IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength)
{ {
/* Set up force-feedback on devices that support it. This is just a stub /* Set up force-feedback on devices that support it. This is just a stub
for the time being, since we don't yet know how the serial port force for the time being, since we don't yet know how the serial port force
@ -67,7 +68,7 @@ void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
DWORD axis; DWORD axis;
LONG direction; LONG direction;
DIEFFECT fx; DIEFFECT fx;
DICONSTANTFORCE cf; DICONDITION cond;
HRESULT hr; HRESULT hr;
assert(dev != NULL); assert(dev != NULL);
@ -77,11 +78,17 @@ void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
dprintf("DirectInput: Starting force feedback (may take a sec)\n"); dprintf("DirectInput: Starting force feedback (may take a sec)\n");
// Auto-centering effect
axis = DIJOFS_X; axis = DIJOFS_X;
direction = 0; direction = 0;
memset(&cf, 0, sizeof(cf)); memset(&cond, 0, sizeof(cond));
cf.lMagnitude = 0; cond.lOffset = 0;
cond.lPositiveCoefficient = strength;
cond.lNegativeCoefficient = strength;
cond.dwPositiveSaturation = strength; // For FG920?
cond.dwNegativeSaturation = strength; // For FG920?
cond.lDeadBand = 0;
memset(&fx, 0, sizeof(fx)); memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx); fx.dwSize = sizeof(fx);
@ -93,20 +100,19 @@ void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
fx.cAxes = 1; fx.cAxes = 1;
fx.rgdwAxes = &axis; fx.rgdwAxes = &axis;
fx.rglDirection = &direction; fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cf); fx.cbTypeSpecificParams = sizeof(cond);
fx.lpvTypeSpecificParams = &cf; fx.lpvTypeSpecificParams = &cond;
hr = IDirectInputDevice8_CreateEffect( hr = IDirectInputDevice8_CreateEffect(
dev, dev,
&GUID_ConstantForce, &GUID_Spring,
&fx, &fx,
&obj, &obj,
NULL); NULL);
if (FAILED(hr)) { if (FAILED(hr)) {
dprintf("DirectInput: DirectInput force feedback unavailable: %08x\n", dprintf("DirectInput: Centering spring force feedback unavailable: %08x\n",
(int) hr); (int) hr);
return; return;
} }
@ -114,15 +120,15 @@ void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
if (FAILED(hr)) { if (FAILED(hr)) {
IDirectInputEffect_Release(obj); IDirectInputEffect_Release(obj);
dprintf("DirectInput: DirectInput force feedback start failed: %08x\n", dprintf("DirectInput: Centering spring force feedback start failed: %08x\n",
(int) hr); (int) hr);
return; return;
} }
*out = obj; *out = obj;
dprintf("DirectInput: Force feedback initialized and set to zero\n"); dprintf("DirectInput: Centering spring effects initialized with strength %d%%\n",
strength / 100);
} }
HRESULT idz_di_dev_poll( HRESULT idz_di_dev_poll(

View File

@ -11,7 +11,7 @@ union idz_di_state {
}; };
HRESULT idz_di_dev_start(IDirectInputDevice8W *dev, HWND wnd); HRESULT idz_di_dev_start(IDirectInputDevice8W *dev, HWND wnd);
void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out); void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength);
HRESULT idz_di_dev_poll( HRESULT idz_di_dev_poll(
IDirectInputDevice8W *dev, IDirectInputDevice8W *dev,
HWND wnd, HWND wnd,

View File

@ -73,6 +73,7 @@ static uint8_t idz_di_gear[6];
static bool idz_di_use_pedals; static bool idz_di_use_pedals;
static bool idz_di_reverse_brake_axis; static bool idz_di_reverse_brake_axis;
static bool idz_di_reverse_accel_axis; static bool idz_di_reverse_accel_axis;
static uint16_t idz_di_center_spring_strength;
HRESULT idz_di_init( HRESULT idz_di_init(
const struct idz_di_config *cfg, const struct idz_di_config *cfg,
@ -171,7 +172,9 @@ HRESULT idz_di_init(
return hr; return hr;
} }
idz_di_dev_start_fx(idz_di_dev, &idz_di_fx); // Convert the strength from 0-100 to 0-10000 for DirectInput
idz_di_dev_start_fx(idz_di_dev, &idz_di_fx,
idz_di_center_spring_strength * 100);
if (cfg->pedals_name[0] != L'\0') { if (cfg->pedals_name[0] != L'\0') {
hr = IDirectInput8_EnumDevices( hr = IDirectInput8_EnumDevices(
@ -346,6 +349,16 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg)
idz_di_gear[i] = cfg->gear[i]; idz_di_gear[i] = cfg->gear[i];
} }
// FFB configuration
if (cfg->center_spring_strength < 0 || cfg->center_spring_strength > 100) {
dprintf("Wheel: Invalid center spring strength: %i\n", cfg->center_spring_strength);
return E_INVALIDARG;
}
idz_di_center_spring_strength = cfg->center_spring_strength;
return S_OK; return S_OK;
} }

View File

@ -67,6 +67,14 @@ void swdc_di_config_load(struct swdc_di_config *cfg, const wchar_t *filename)
L"reverseAccelAxis", L"reverseAccelAxis",
0, 0,
filename); filename);
// FFB configuration
cfg->center_spring_strength = GetPrivateProfileIntW(
L"dinput",
L"centerSpringStrength",
30,
filename);
} }
void swdc_xi_config_load(struct swdc_xi_config *cfg, const wchar_t *filename) void swdc_xi_config_load(struct swdc_xi_config *cfg, const wchar_t *filename)

View File

@ -19,6 +19,9 @@ struct swdc_di_config {
uint8_t wheel_yellow; uint8_t wheel_yellow;
bool reverse_brake_axis; bool reverse_brake_axis;
bool reverse_accel_axis; bool reverse_accel_axis;
// FFB configuration
uint16_t center_spring_strength;
}; };
struct swdc_xi_config { struct swdc_xi_config {

View File

@ -44,7 +44,8 @@ HRESULT swdc_di_dev_start(IDirectInputDevice8W *dev, HWND wnd)
return hr; return hr;
} }
void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out) void swdc_di_dev_start_fx(
IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength)
{ {
/* Set up force-feedback on devices that support it. This is just a stub /* Set up force-feedback on devices that support it. This is just a stub
for the time being, since we don't yet know how the serial port force for the time being, since we don't yet know how the serial port force
@ -67,7 +68,7 @@ void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
DWORD axis; DWORD axis;
LONG direction; LONG direction;
DIEFFECT fx; DIEFFECT fx;
DICONSTANTFORCE cf; DICONDITION cond;
HRESULT hr; HRESULT hr;
assert(dev != NULL); assert(dev != NULL);
@ -77,11 +78,17 @@ void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
dprintf("DirectInput: Starting force feedback (may take a sec)\n"); dprintf("DirectInput: Starting force feedback (may take a sec)\n");
// Auto-centering effect
axis = DIJOFS_X; axis = DIJOFS_X;
direction = 0; direction = 0;
memset(&cf, 0, sizeof(cf)); memset(&cond, 0, sizeof(cond));
cf.lMagnitude = 0; cond.lOffset = 0;
cond.lPositiveCoefficient = strength;
cond.lNegativeCoefficient = strength;
cond.dwPositiveSaturation = strength; // For FG920?
cond.dwNegativeSaturation = strength; // For FG920?
cond.lDeadBand = 0;
memset(&fx, 0, sizeof(fx)); memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx); fx.dwSize = sizeof(fx);
@ -93,20 +100,19 @@ void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
fx.cAxes = 1; fx.cAxes = 1;
fx.rgdwAxes = &axis; fx.rgdwAxes = &axis;
fx.rglDirection = &direction; fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cf); fx.cbTypeSpecificParams = sizeof(cond);
fx.lpvTypeSpecificParams = &cf; fx.lpvTypeSpecificParams = &cond;
hr = IDirectInputDevice8_CreateEffect( hr = IDirectInputDevice8_CreateEffect(
dev, dev,
&GUID_ConstantForce, &GUID_Spring,
&fx, &fx,
&obj, &obj,
NULL); NULL);
if (FAILED(hr)) { if (FAILED(hr)) {
dprintf("DirectInput: DirectInput force feedback unavailable: %08x\n", dprintf("DirectInput: Centering spring force feedback unavailable: %08x\n",
(int) hr); (int) hr);
return; return;
} }
@ -114,15 +120,15 @@ void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
if (FAILED(hr)) { if (FAILED(hr)) {
IDirectInputEffect_Release(obj); IDirectInputEffect_Release(obj);
dprintf("DirectInput: DirectInput force feedback start failed: %08x\n", dprintf("DirectInput: Centering spring force feedback start failed: %08x\n",
(int) hr); (int) hr);
return; return;
} }
*out = obj; *out = obj;
dprintf("DirectInput: Force feedback initialized and set to zero\n"); dprintf("DirectInput: Centering spring effects initialized with strength %d%%\n",
strength / 100);
} }
HRESULT swdc_di_dev_poll( HRESULT swdc_di_dev_poll(

View File

@ -11,7 +11,7 @@ union swdc_di_state {
}; };
HRESULT swdc_di_dev_start(IDirectInputDevice8W *dev, HWND wnd); HRESULT swdc_di_dev_start(IDirectInputDevice8W *dev, HWND wnd);
void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out); void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength);
HRESULT swdc_di_dev_poll( HRESULT swdc_di_dev_poll(
IDirectInputDevice8W *dev, IDirectInputDevice8W *dev,
HWND wnd, HWND wnd,

View File

@ -70,6 +70,7 @@ static uint8_t swdc_di_wheel_yellow;
static bool swdc_di_use_pedals; static bool swdc_di_use_pedals;
static bool swdc_di_reverse_brake_axis; static bool swdc_di_reverse_brake_axis;
static bool swdc_di_reverse_accel_axis; static bool swdc_di_reverse_accel_axis;
static uint16_t swdc_di_center_spring_strength;
HRESULT swdc_di_init( HRESULT swdc_di_init(
const struct swdc_di_config *cfg, const struct swdc_di_config *cfg,
@ -168,7 +169,9 @@ HRESULT swdc_di_init(
return hr; return hr;
} }
swdc_di_dev_start_fx(swdc_di_dev, &swdc_di_fx); // Convert the strength from 0-100 to 0-10000 for DirectInput
swdc_di_dev_start_fx(swdc_di_dev, &swdc_di_fx,
swdc_di_center_spring_strength * 100);
if (cfg->pedals_name[0] != L'\0') { if (cfg->pedals_name[0] != L'\0') {
hr = IDirectInput8_EnumDevices( hr = IDirectInput8_EnumDevices(
@ -320,6 +323,16 @@ static HRESULT swdc_di_config_apply(const struct swdc_di_config *cfg)
swdc_di_reverse_brake_axis = cfg->reverse_brake_axis; swdc_di_reverse_brake_axis = cfg->reverse_brake_axis;
swdc_di_reverse_accel_axis = cfg->reverse_accel_axis; swdc_di_reverse_accel_axis = cfg->reverse_accel_axis;
// FFB configuration
if (cfg->center_spring_strength < 0 || cfg->center_spring_strength > 100) {
dprintf("Wheel: Invalid center spring strength: %i\n", cfg->center_spring_strength);
return E_INVALIDARG;
}
swdc_di_center_spring_strength = cfg->center_spring_strength;
return S_OK; return S_OK;
} }