USB: Add train controller emulation
Description of Changes
Emulate a range of "Densha Mascon" train controllers originally sold by Taito, such as TCP20009, TCP20011, TCP20014. Built using datasheets courtesy of @marcriera and other contributors at https://marcriera.github.io/ddgo-controller-docs/ .
| Type 2 | Shinkansen | Ryojōhen |
|---|---|---|
Rationale behind Changes
Lever-based controllers provide an advantage over the standard DualShock emulation for some games, as these controllers physically hold an axis in a particular level without requiring the player to actively apply pressure. These changes allow players with lever-based controllers to play Densha de Go! titles such as:
- Densha de GO! 3 Tsūkin-hen
- Densha de GO! Final
- Densha de GO! Professional 2
- Densha de GO! Ryojōhen
- Densha de GO! San'yō Shinkansen-hen
Suggested Testing Steps
The ddgo-controller-docs website describes these controllers and lists the compatible titles. I have tested these with my own USB Zuiki One-Handle MasCon controller for Nintendo Switch. Given the way that the hardware maps into the emulated controller, the best subtype for this particular hardware controller is the "Type 2" controller. Some games will change the input scheme and number of power/brake notch settings based on the selected subtype variant.
Related: #4763
cc @sonik-br this might be interesting to you. I have not tested the "USB passthrough" mode but I believe that this should allow original Densha De GO! USB controllers to work correctly with this patch.
Fixed a couple of CI complaints - extraneous xml tags in the vcxproj file, and I forgot to add the new files to the CMakeLists.txt, leading to linker failures in the CI builds. These should be resolved now.
cc @sonik-br this might be interesting to you. I have not tested the "USB passthrough" mode but I believe that this should allow original Densha De GO! USB controllers to work correctly with this patch.
Amazing work! I can try and report back. There's a build to download or I need to build from source?
There's a build to download or I need to build from source?
You can download the CI artifacts from the checks tab, make sure to pick your OS.
Not sure how to use usb passthrough. The device is not HID and does not shows as a gamepad under windows. I need to install generic a usb driver with zadig?
Not sure how to use usb passthrough. The device is not HID and does not shows as a gamepad under windows. I need to install generic a usb driver with zadig?
Won't work. Only HID controllers like DGOC-44U will work
Won't work. Only HID controllers like DGOC-44U will work
Ah ok! I can emulate this device using a rp2040 then. Can try it later this week.
@Florin9doi @IlDucci thanks for the reviews! I believe I've addressed all of your comments.
I could no get passthrough to work. I'm emulating a PC Two Handle (DGOC-44U) using the descriptors from marcriera. Need any special setting?
What kind of passthrough do you expect? You'll still have to map the axes and buttons , but the axes values won't stick to predefined values and will be sent unchanged.
What kind of passthrough do you expect? You'll still have to map the axes and buttons , but the axes values won't stick to predefined values and will be sent unchanged.
Brake axis can't be mapped. It does not pick any movement on the map screen. But the axis is working as I can test on windows and on the PC version of the game (DDGO Final)
The brake's range is too limited and is ignored? Try to map a random button then open PCSX2.ini and change Brake=Xinput-X/ButtonY to Brake=Dinput-x/FullAxis1
Still not working. Tested with fullaxis0 and fullaxis1.
[USB1]
Type = DenshaCon
DenshaCon_A = DInput-0/Button1
DenshaCon_B = DInput-0/Button0
DenshaCon_C = DInput-0/Button2
DenshaCon_D = DInput-0/Button3
DenshaCon_Select = DInput-0/Button4
DenshaCon_Start = DInput-0/Button5
DenshaCon_Up = DInput-0/Button6
DenshaCon_Right = DInput-0/Button9
DenshaCon_Down = DInput-0/Button7
DenshaCon_Left = DInput-0/Button8
DenshaCon_Power = DInput-0/+Axis1~
DenshaCon_Brake = DInput-0/FullAxis0
DenshaCon_Passthrough = true
Also tested with
DenshaCon_Power = DInput-0/FullAxis1
DenshaCon_Brake = DInput-0/FullAxis0
@Florin9doi @IlDucci I've resolved your latest feedback 🚀
@sonik-br thanks for testing. Do the other buttons and power lever work in-game or are all of the controls broken? If they work, does the lever increase the number of power notches that you expect?
I also wonder if the brake axis is a much higher number. For my controller, despite there being only a single lever with two axes (power, brake), SDL represents these with positive/negative axis 7.
@joestringer buttons and dpad works in-game.
Power lever does not. The game think it's always on max power.
@sonik-br Hmm OK. Early on I did encounter similar issues while initially developing the support. I can't do much about the brake since that relies on the input library to pull the values into the point where this feature processes it. Unless you're able to resolve that problem, the idea of the passthrough mode feature/patch might not make sense.
That said to debug further about what's going on with the power axis, you could try pulling this new version with debug logging here:
https://github.com/joestringer/pcsx2/actions
You might need to remap the controls again, I'm not sure. But after that you can go to Tools -> Enable Log Window or Enable File Logging. When you run the game, the log will spam lines about the brake and power values. If you can start with the controller with brakes and power off, then move the power lever step by step from released up to full power and back down, it will log all the input values. If you scan up through the logs until you see logs printing different values for the power axis and share those logs, it would help to understand the range of values that are being pulled into this code. Separately if you can figure out the brake, then it would also help to have the same log data lines with the varying brake input values.
@joestringer tested... Forcing it on the ini file to fullaxis as:
TrainController_Power = DInput-0/FullAxis1
TrainController_Brake = DInput-0/FullAxis0
Brake only shows as FF on it's first position and FF on all other positions. Power shows as:
P5 80 P4 A0 P3 Be P2 D3 P1 ED N FF Transiton FF
All tests doe using direct input.
And interesting: Same results with and without passthrough mode
And interesting: Same results with and without passthrough mode
Oh yeah maybe I didn't explain well. The logs you see are presenting the raw input from DInput, before this code processes the values to output to the game. So the passthrough mode doesn't have an effect on the values you see there. That's normal.
I can't really explain the discrepancy.. If you're running a DGOC-44U, then these docs describe the notches as:
| N | P1 | P2 | P3 | P4 | P5 | Transition |
|---|---|---|---|---|---|---|
| 0x81 | 0x6D | 0x54 | 0x3F | 0x21 | 0x00 | 0xFF |
Comparing this with the values that you have presented, there values are very different:
| N | P1 | P2 | P3 | P4 | P5 | Transition |
|---|---|---|---|---|---|---|
| 0xFF | 0xED | 0xD3 | 0xBe | 0xA0 | 0x80 | 0xFF |
The passthrough mode was written with the idea in mind that the values from the input library would be identical to the selected subtype of controller. But even if I look through the other controller types in the Marc Riera datasheets, I can't find any controller types that match the values you are reporting.
One interesting thing about the bitpatterns you report is that the bit at offset 0x80 is always set. On average across the values they're all around 0x7E-0x80 higher than the value reported by the docs. This makes me wonder whether either model you have works differently somehow, or if perhaps the values get treated as signed values that get set negative somewhere, or if somehow the input library is doing some conversions that cause a skewing of the values into the upper range.. Not sure if FullAxis vs. +Axis vs -Axis configuration could have this sort of impact as well.
Technically it's not that difficult to try to adjust for this, but it's a bit hard for me to picture the range of devices that may be impacted by this behaviour - is this a general property of all of the original controllers so I should apply a filter automatically in passthrough mode? Or is it that the axis needs to be configured in a particular way to cause these results? Will a fix I come up with help or hurt the emulation of this controller? I'd welcome input from upstream PCSX2 devs if they have ideas on how to best handle this. I guess I could add another separate setting option called "DGOC-44U compatibility" if we think that it's this specific controller that has this behaviour. On the other hand, another option is just to remove passthrough mode if it's not going to fully work for someone.
In the mean time, just to see if it could work, I added a hack to the latest builds over here to try to have the right behaviour for your environment: https://github.com/joestringer/pcsx2/actions . The code is in https://github.com/joestringer/pcsx2/pull/2.
Brake only shows as FF on it's first position and FF on all other positions.
OK. That confirms that Dinput isn't processing the brake signal at all. Pretty much what we expected since you can't even configure it through the UI.
@joestringer I'm not exactly using a real "DGOC-44U"... It's a PS2 Type 2 device with a custom made usb adapter that outputs like a DGOC-44U. But I don't think that's the problem as the PC version of the game detects and uses the device correctly.
Btw looks like the power axis is working as half axis, right? It goes from 0x80 to 0xff. It's like if dinput apply some kind of range calibration.
Try to switch from dinput to sdl
Testing under windows joy.cpl
Neutral
P5
Yeah if you can try SDL, that would be good. But I would not suggest using the latest build I shared from my GitHub fork, maybe try the previous one that you tested earlier with the debug logging, or just pick up the build from this PR. The latest build on my fork will mess with the input as it gets passed through.
joy.cpl output there looks good, matches the spec I expected.
SDL raw, with the same build as before P5 ff P4 be P3 82 P2 57 P1 24 N 00 Transiton 00
Those latest numbers look more like the scale is inverted and spread across the full range of a byte rather than limiting to around half the range. That I could see being part of how the input is processed and rendered into floats before handing to this patch.
Is that configuration with a half axis or a full axis configured? Also, does brake work with SDL or do you see the same issue with the brakes?
Shinkansen controller USB passthrough isn't working - it says both handles have a value of 0, and actually moving the physical controller does nothing. Especially annoying, as "removing" the virtual USB freezes the game.
@mario032106 thanks for trying it out! Would you be able to also share the console logs, similar to the information I asked for in this comment? There could be something fairly simple that I got wrong since I don't have the hardware to test with. You can try with these builds (windows link) or wait for non-Windows builds at the top here.
@joestringer Used that exact build, and had it log to console. Both handles started at 0. No input was registered, not even the face buttons. Mapped it to the keyboard to check if it even worked, and it did, so either it's a problem with my controller (unlikely, as AutoTrainTAS recognizes it just fine, as does Final on an actual PS2) or USB passthrough.
@mario032106 and to confirm is that using SDL for the input? The logs print the raw values pulled from the input library, so if those are printing zeroes regardless of the input then it's due to an issue in the code before this code even gets executed. The log is just printing what values hit this code, before this code processes the values.
Shinkansen controller USB passthrough isn't working - it says both handles have a value of 0, and actually moving the physical controller does nothing. Especially annoying, as "removing" the virtual USB freezes the game.
There is no USB passthrough , only axis passthrough. A real Shinkansen wont work unless you have a 3rd party HID driver