elishacloud / dinputto8

A dll module that is designed to improve compatibility in games using DirectInput 1-7 (dinput.dll) by converting all API calls to their equivalent DirectInput 8 (dinput8.dll) ones. This allows older games to be able to use newer tools and wrappers written for DirectInput 8.
zlib License
116 stars 11 forks source link

Device enumeration change between Win98 and WinXP #27

Closed NicknineTheEagle closed 5 months ago

NicknineTheEagle commented 6 months ago

Further investigation into Polaris SnoCross has revealed that something changed about DirectInput device enumeration in Windows XP. Under Windows 98, DirectInput lists USB keyboards and mice if you pass DIEDFL_INCLUDEALIASES flag with USB devices having the usual device type but with HID flag, e.g. 0x00010102. Under Windows XP, USB keyboards and mice are always listed and their type is set to 1 (DIDEVTYPE_DEVICE), so 0x00010001. This notably makes Polaris SnoCross crash if you have USB keyboard or mouse plugged in as its code breaks if it encounters device type that isn't 2, 3, or 4. This seems like an issue within the scope of dinputto8 although I'm not sure what the best way to handle this is.

elishacloud commented 5 months ago

@NicknineTheEagle, I was trying to get away from requiring settings for this. I believe that more games will probably require the Windows 95/98 way because Windows XP shipped with DirectX 8.1, so presumably most games written for Windows XP use DInput8 rather than DInput. Therefore I recommend we change this to work the way Windows 98 did.

NicknineTheEagle commented 5 months ago

Right, we can filter out HID device of type 1 by default but if we also want to implement legacy "include aliases" behavior, that's gonna be a problem because how will we know the real type of USB device?

elishacloud commented 5 months ago

I am not sure what part of this is making Polaris SnoCross crash. Maybe if we filter out HID device of type 1 unless DIEDFL_INCLUDEALIASES flag is used. Then if DIEDFL_INCLUDEALIASES flag is used we can convert the type to HID flag, e.g. 0x00010102.

Basically, if we cannot completely replicate Windows 98 (because we cannot easily know which are USB devices) we can at least replicate part of what Windows 98 does, enough to get the games working. Does that make sense?

The goal of dinputto8 is for compatibility not necessarily to replicate the exact behavior of older Windows.

NicknineTheEagle commented 5 months ago

I am not sure what part of this is making Polaris SnoCross crash.

As I said in the OP, Polaris SnoCross crashes if it encounters device type that is not 2, 3 and 4.

Then if DIEDFL_INCLUDEALIASES flag is used we can convert the type to HID flag, e.g. 0x00010102.

Well, that's the question I've raised, how are we going to convert the type if we don't "know" the proper device type to begin with and all we got is 0x00010001? I don't think there are any additional fields that would let us extrapolate it.

elishacloud commented 5 months ago

I believe we can use the wUsagePage and wUsage fields in the DIDEVICEINSTANCE structure to differentiate between a mouse and a keyboard when dwDevType is 0x00010001 (DIDEVTYPE_DEVICE | DIDEVTYPE_HID). These fields follow the USB HID usage tables, which provide detailed information about the type of device.

Here's how you can interpret these fields:

For USB HID devices:

So, you can differentiate between a mouse and a keyboard by checking the values of wUsagePage and wUsage.

Here is how you can implement this in code:

if (pDeviceInstance->dwDevType == 0x00010001) // DIDEVTYPE_DEVICE | DIDEVTYPE_HID
{
    if (pDeviceInstance->wUsagePage == 0x01) // Generic Desktop Controls
    {
        switch (pDeviceInstance->wUsage)
        {
            case 0x02:
                // This is a mouse
            case 0x06:
                // This is a keyboard
            case 0x04:
                // This is a joystick
            case 0x05:
                // This is a gamepad
            default:
                // Other device types within the Generic Desktop Controls page
        }
    }
    else
    {
        // Other usage pages
    }
}
else
{
    // Other device types
}

My suggestion is that if device type is 1 then we look at wUsagePage and wUsage fields. If it is a keyboard then we change it to 3 (DIDEVTYPE_KEYBOARD). If it is a mouse then we change it to a 2 (DIDEVTYPE_MOUSE). If it is a joystick or gamepad then we change it to 4 (DIDEVTYPE_JOYSTICK).

If it is not a keyboard, mouse, joystick or gamepad then we exclude it.

Also, I am not sure what diVersion the game is running at, but I wonder if we should only exclude type 1 devices when the game is running a specific version of DirectDraw?

elishacloud commented 5 months ago

I think we probably only want to do this change if diVersion < 0x0500. Presumably Polaris SnoCross is running a version lower than 0x0500.

NicknineTheEagle commented 5 months ago

Polaris SnoCross uses DI version 0x0500.

elishacloud commented 5 months ago

Polaris SnoCross uses DI version 0x0500.

Ok, interesting. I just hate to filter out devices that other games may use. But maybe we need to do this for all DI versions.

elishacloud commented 5 months ago

From the header file the DIEDFL_INCLUDEALIASES is only available on 0x050a or greater.

#if(DIRECTINPUT_VERSION >= 0x050a)
#define DIEDFL_INCLUDEALIASES   0x00010000
#define DIEDFL_INCLUDEPHANTOMS  0x00020000
#endif /* DIRECTINPUT_VERSION >= 0x050a */

I wonder if a better solution is to filter the input? What happens if we filter the below flags out if the DI version is less then 0x050a?

#define DIEDFL_INCLUDEALIASES   0x00010000
#define DIEDFL_INCLUDEPHANTOMS  0x00020000
#define DIEDFL_INCLUDEHIDDEN    0x00040000
NicknineTheEagle commented 5 months ago

I don't see the point in filtering flags out since they didn't exist before 0x050a to begin with and thus, games can't send them. SnoCross doesn't use them in case you're wondering.

elishacloud commented 5 months ago

Under Windows 98, DirectInput lists USB keyboards and mice if you pass DIEDFL_INCLUDEALIASES flag with USB devices having the usual device type but with HID flag

Ok, I misunderstood this comment. I thought the game was sending that flag and it was running DI version 0x0500 and maybe the flag was treated differently between Win98 and WinXP.

In that case, does it make sense to only modify the devices with type 1 if the DIEDFL_INCLUDEALIASES flag is NOT sent?

Edit: basically, I would like to surgically fix this so that maybe we can avoid breaking other games, if that makes sense.

elishacloud commented 5 months ago

Sorry for the continuous messages here!

After thinking about this for a while I think this must be a bug in the game because I think that even in Windows 98 it was possible to have a device type 1. However, it was not quite as common.

So to fix this I suggest we change:

  1. The device type from 0x00010001 to 0x00010002 or 0x00010102 if we can detect from wUsage that it is a mouse.
  2. The device type from 0x00010001 to 0x00010003 or 0x00010103 if we can detect from wUsage that it is a keyboard.
  3. The device type from 0x00010001 to 0x00010004 or 0x00010104 if we can detect from wUsage that it is a joystick or gamepad.

And otherwise we leave the device as is (we don't filter it) and go ahead and pass the device 0x00010001 type through. I think this will offer the best functionality for all games. In most cases this will prevent Polaris SnoCross from crashing, just like on Windows 98.

If you want to make a Pull Request for this go ahead. Otherwise I can put a fix in for this later.

NicknineTheEagle commented 5 months ago

After thinking about this for a while I think this must be a bug in the game because I think that even in Windows 98 it was possible to have a device type 1. However, it was not quite as common.

Yes, you would be right in thinking that cause DIDEVTYPE_DEVICE looks like it always existed in DirectInput so it's on devs for not handling it properly. Also worth noting that the game does officially support Windows XP according to the back of the box.

So to fix this I suggest we change

Sounds like a plan, I'll look into this soon.

elishacloud commented 5 months ago

I put in an update for this issue. Let me know if this helps resolve the issue with Polaris SnoCross and USB mice/keyboards.

NicknineTheEagle commented 5 months ago

I put in an update for this issue. Let me know if this helps resolve the issue with Polaris SnoCross and USB mice/keyboards.

1) This doesn't cover GetCapabilities and GetDeviceInfo, the former of which is used by SnoCross. 2) You're not setting device subtype.

elishacloud commented 5 months ago

Good catch. I missed that. Updated code to fix the device and device subtype in all three functions. Let me know if this works better with Polaris SnoCross.

elishacloud commented 5 months ago

I put a couple more fixes in. Hopefully the latest build will fix this issue.

elishacloud commented 5 months ago

@NicknineTheEagle, if you get a chance, please let me know if the latest build fixes the issue here. Thanks.

elishacloud commented 5 months ago

The latest release should have the fix for this. If there is still an issue you can reopen this issue.

NicknineTheEagle commented 5 months ago

@elishacloud Sorry about the delay, I was preoccupied. I have tested https://github.com/elishacloud/dinputto8/commit/954949d7cb96ed63334665ac99f4903369e716e3 and the crash in Polaris SnoCross is not fixed. Issue should be re-opened.

elishacloud commented 5 months ago

No problem. Can you give me more details on what what part of the fix is not working?

NicknineTheEagle commented 5 months ago

It looks like DIDEVTYPE_DEVICE conversion is not working properly cause I'm still seeing 0x00010001 being returned in the debugger. I'm guessing wUsagePage or wUsage do not fall under handled categories on my PC, that's the only reason I see why it wouldn't work.

NicknineTheEagle commented 5 months ago

From looking at my own devices, it looks like each of them is listed multiple times with different HID usage pages and HID_USAGE_PAGE_GENERIC isn't even necessarily present. I think it's better to just completely filter out DIDEVTYPE_DEVICE rather than trying to untangle this mess.

NicknineTheEagle commented 5 months ago

Here's what shows up on my desktop with USB mouse and keyboard (device type, device name, usage page, usage):

0112 Mouse 00 00
0413 Keyboard 00 00
10011 USB Device 0c 01
10011 Corsair Gaming K70 LUX RGB Keyboard  0c 01
10011 Corsair Gaming K70 LUX RGB Keyboard  ffc0 02
10011 Corsair Gaming K70 LUX RGB Keyboard  ffc2 03
10011 Corsair Gaming K70 LUX RGB Keyboard  ffc2 04
10011 USB Device 01 80

Here's what shows up on my laptop with touchpad and USB mouse:

0112 Mouse 00 00
0413 Keyboard 00 00
10215 Controller (Rumble Gamepad F510) 01 05
10011 USB Receiver 0c 01
10011 USB Receiver ff00 01
10011 USB Receiver ff00 02
10011 HIDI2C Device 0d 0e
elishacloud commented 5 months ago

It looks like the only solution is to filter device type 0x00010001. I am a bit hesitant because I am not sure if it will break anything.

elishacloud commented 5 months ago

Ok, the latest change should fix it. I'll filter the devices for now and we can see if it causes any issues in the future.

NicknineTheEagle commented 5 months ago

Ok, the game starts now but doesn't react to input - it's like no input devices are found.

elishacloud commented 5 months ago

Sorry about that. I made a slight mistake in the last check-in. It should be fixed now.

NicknineTheEagle commented 5 months ago

Ok, yeah, that looks like it fixed it.

elishacloud commented 5 months ago

Ok, very good. Then I am closing this.