NFSIISE: Original game bug: Gamepads with more than 15 buttons can't bind buttons
Disclaimer: I don’t know if this is fixed in the project currently, I was only pointed at it when I tweeted about the original game bug with gamepad bindings. It goes as follows:
- The game polls gamepad button like so - notice the presence of a
for
loop with a signed int16_t acting as the “number of buttons” (sub_404360
in my executable):
if ( *(_WORD *)((char *)&word_512F34 + v17 + 2) )
{
v3 = PollJoystickButtons(v22, a1);
if ( v3 )
{
v4 = v20;
v5 = 1;
for ( i = 0; i < *(int *)((char *)&word_512F34 + v17) >> 16 && !v1; ++i )
{
if ( (v5 & v3) != 0 )
v1 = (v4 & 0xFFFFFF00) + 2;
else
v5 *= 2;
v4 += 256;
}
while ( PollJoystickButtons(v22, v5) )
sub_483EB0();
}
- This int16 is populated with… a button bitmask populated by calling
IDirectInputDevice::GetObjectInfo
in a loop, like so (sub_421BD0
in my executable):
v9 = 48;
do
{
if ( v8 >= 32 )
break;
v10 = *(int *)&v25[1] >> 24;
if ( !pDirectInputJoystick[*(int *)&v25[1] >> 24]->lpVtbl->GetObjectInfo(
pDirectInputJoystick[*(int *)&v25[1] >> 24],
&v18,
v9,
1) )
{
v11 = (1 << v8) | dword_4E7388[23 * v10];
dword_4E7388[23 * v10] = v11;
if ( a2 )
*a2 = v11;
++*(_DWORD *)v25;
}
++v9;
++v8;
}
while ( *(_DWORD *)v25 < v22 );
The problem is - since this int16 representing the button count gets populated with a button bitmask, gamepads reporting 16 or more buttons stash a value FFFF
in that count, that then gets interpreted as -1
by the for loop. The result - gamepad buttons cannot be bound at all.
I see two ways to fix it:
- If you can modify the original game code - modify the code snippet from 2. so
dword_4E7388
becomes a button count and not a button bitmask. This is the approach I’m going to take in my standalone fix. - If you absolutely cannot modify the original game code - spoof DirectInput capabilities and adjust gamepad’s
GetObjectInfo
wrappers to never return more than 15 buttons. This will ensure that the button bitmask is at most7FFF
and therefore avoid underflow. I kind of recommend against this approach, because the game will still potentially do an equivalent offor ( i = 0; i < 32767; i++ )
every time it checks button presses.
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Comments: 18 (9 by maintainers)
Ah, back then I misunderstood what did you mean by “patching C code”. Either way, it’s all solved now. Thanks!
Thanks for the PR!
Here if possible