samuelgr / Xidi

DirectInput interface for XInput controllers
BSD 3-Clause "New" or "Revised" License
180 stars 10 forks source link

FFB not working on Midnight Club II (log included) #68

Open instinctualjealousy opened 12 months ago

instinctualjealousy commented 12 months ago

I can confirm it works (not well) in XInputPlus and it also works (decently) with that Japanese FFB driver/native dinput8. The game definitely supports it. I've been moving things over to Xidi where possible. I started the game up and did various things (hard driving, crashing into stuff at the end). I do see some effects in the log. The game does not appear to have a toggle for it.

Xidi_DInput8_mc2.exe_1600.log

instinctualjealousy commented 12 months ago

I'm not going to open another issue for this, but FFB is also not working in Ford Racing 2 for me- I uninstalled the FFB driver just in case of conflicts. Xidi is indeed retrieving FFB data according to this log.

Xidi_DInput8_fr2.exe_11232.log

samuelgr commented 12 months ago

For Midnight Club II, I think this is a case of Xidi's existing implementation being slightly different than what DirectInput actually does.

I see this in the log:

[10/09/2023 12:20:48] [D] Creating effect with GUID Square.
[10/09/2023 12:20:48] [D] Begin dump of effect parameters.
[10/09/2023 12:20:48] [D]   Control:
[10/09/2023 12:20:48] [D]     flags = 0x000003ff (DIEP_ALLPARAMS)
[10/09/2023 12:20:48] [D]   Basics:
[10/09/2023 12:20:48] [D]     dwSize = 56 (sizeof(DIEFFECT))
[10/09/2023 12:20:48] [D]     dwFlags = 0x00000012 (DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS)
[10/09/2023 12:20:48] [D]     dwDuration = 4294967295 (INFINITE)
[10/09/2023 12:20:48] [D]     dwSamplePeriod = 0
[10/09/2023 12:20:48] [D]     dwGain = 10000
[10/09/2023 12:20:48] [D]     dwStartDelay = 0
[10/09/2023 12:20:48] [D]     dwTriggerButton = 4294967295 (DIEB_NOTRIGGER)
[10/09/2023 12:20:48] [D]     dwTriggerRepeatInterval = 0
[10/09/2023 12:20:48] [D]   Axes:
[10/09/2023 12:20:48] [D]     cAxes = 2
[10/09/2023 12:20:48] [D]     rgdwAxes[ 0] = 0x0000 (unable to identify)
[10/09/2023 12:20:48] [D]     rgdwAxes[ 1] = 0x0004 (unable to identify)
[10/09/2023 12:20:48] [D]   Direction:
[10/09/2023 12:20:48] [D]     cAxes = 2
[10/09/2023 12:20:48] [D]     rglDirection[ 0] = 1
[10/09/2023 12:20:48] [D]     rglDirection[ 1] = 0
[10/09/2023 12:20:48] [D]   Envelope:
[10/09/2023 12:20:48] [D]     lpEnvelope->dwSize = 20 (sizeof(DIENVELOPE))
[10/09/2023 12:20:48] [D]     lpEnvelope->dwAttackLevel = 0
[10/09/2023 12:20:48] [D]     lpEnvelope->dwAttackTime = 0
[10/09/2023 12:20:48] [D]     lpEnvelope->dwFadeLevel = 0
[10/09/2023 12:20:48] [D]     lpEnvelope->dwFadeTime = 0
[10/09/2023 12:20:48] [D]   Type-Specific:
[10/09/2023 12:20:48] [D]     cbTypeSpecificParams = 16 (sizeof(DIPERIODIC))
[10/09/2023 12:20:48] [D]     lpvTypeSpecificParams->dwMagnitude = 0
[10/09/2023 12:20:48] [D]     lpvTypeSpecificParams->lOffset = 0
[10/09/2023 12:20:48] [D]     lpvTypeSpecificParams->dwPhase = 0
[10/09/2023 12:20:48] [D]     lpvTypeSpecificParams->dwPeriod = 0
[10/09/2023 12:20:48] [D] End dump of effect parameters.
[10/09/2023 12:20:48] [I] Invoked Xidi::VirtualDirectInputDevice<0>::CreateEffect() on interface object 0 associated with Xidi virtual controller 1, result = 0x80070057.

The result of trying to create an effect is always 0x80070057 (DIERR_INVALIDPARAM), meaning that Xidi looks at the parameters and rejects the effect as invalid. I believe the reason is under the "Axes" part of the parameters dump, where Xidi tries to identify the two axes but fails ("unable to identify"). Since the identification method is by object offset (DIEFF_OBJECTOFFSETS in the flags), that means that Xidi believes it doesn't know the offsets, which means as far as Xidi is concerned the application's data format (where the game defines all the offsets) isn't set.

However, earlier in the log:

[10/09/2023 12:20:06] [I] Invoked Xidi::VirtualDirectInputDevice<1>::SetDataFormat() on interface object 2 associated with Xidi virtual controller 1, result = 0x00000000.

The difference is "interface object 2" for SetDataFormat vs "interface object 0" for CreateEffect, even though the associated virtual controller is the same. The way Xidi is currently implemented is it keeps all properties and state, including data format, local to each individual interface object. However, it may be the case that DirectInput shares one data format among all interface objects, in which case the fix to this issue is to change Xidi to do the same thing.

I'll need to do some tests with DirectInput itself to figure out how it handles data formats in this instance.

samuelgr commented 11 months ago

It turns out that DirectInput itself does exactly what Xidi does: keeps data formats local to individual objects.

I wrote a test program that creates two interface objects for the same device, set the data format on one of them but not the other, and then called CreateEffect. If I call it on the interface object with the data format set, it succeeds, but if I call it on the other interface object, it fails.

I'm not sure exactly how the game is getting away with creating effects successfully, but it is possible DirectInput itself provides a built-in workaround for this game. The FF driver would be able to take advantage of that (since it just plugs into DirectInput), and XInput Plus would be doing its own thing entirely. I don't think this is a bug in Xidi, but fixing the game's FF effects will require a workaround to be implemented.