-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
USB: Add train controller emulation #11723
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for submitting a contribution to PCSX2
As this is your first pull request, please be aware of the contributing guidelines.
Additionally, as per recent changes in GitHub Actions, your pull request will need to be approved by a maintainer before GitHub Actions can run against it. You can find more information about this change here.
Please be patient until this happens. In the meantime if you'd like to confirm the builds are passing, you have the option of opening a PR on your own fork, just make sure your fork's master branch is up to date!
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's some suggestions to make life a bit easier for translators.
Amazing work! |
Not sure how to use usb passthrough. The device is not HID and does not shows as a gamepad under windows. |
Won't work. Only HID controllers like DGOC-44U will work |
Ah ok! |
@Florin9doi @IlDucci thanks for the reviews! I believe I've addressed all of your comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for hearing translators out! Unfortunately, I missed one element that could probably use a hint for translators (who might not expect train stuff happening in an emulator).
I could no get passthrough to work. |
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. |
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.
Also tested with
|
@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. |
@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 |
@joestringer tested...
Brake only shows as FF on it's first position and FF on all other positions. P5 80 All tests doe using direct input. 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:
Comparing this with the values that you have presented, there values are very different:
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 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 joestringer#2.
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. |
I may have underestimated the complexity of connecting this logic with real-world DDG controllers ;) Sorry about that. This PR was initially designed just so I could get my Zuiki controller to work. The Zuiki connects like a regular joystick controller into Windows, then outputs various axis values through the positive and negative Y axes when I connect it using SDL. With the passthrough mode turned off, that maps accurately into the game and I can play without any issues. I assume that any other joystick which outputs values in a similar manner should work the same, potentially including things like HOTAS controllers (though I also don't have one of those to try out). What I didn't realize is that some of these older joysticks do not implement HID, and that there may be issues with some of the input libraries in getting the signal into the code that this PR touches, whether SDL, Xinput or Dinput. When I sketched up the patch for axis passthrough, I knew that the baseline functionality made different assumptions about the joystick's output value range compared with the spec sheets, so in an ideal world I would take whatever signal that those controllers provided on the relevant axis and send it straight to the game. My naive hope was that this would "just work", but I don't have the physical hardware to test & debug it. As y'all have provided feedback, it's becoming more and more obvious that there are subtleties to the problem space that I just don't know about. I don't necessarily know enough at the moment to be able to solve all the problems for everyone. For @sonik-br's report, I could potentially get it working by tweaking the options to allow (a) inverting the axis and (b) scaling the full 256-value axis down into a 128-bit output value to the game. I can throw that together. That said, if the brake is still not pushing a full range of values into the controller emulation layer through SDL then I can't solve that. In the end you're gonna want to use both power and brake to actually play. Similarly, for @mario032106, if you can get both axes configured in the settings window for power/brake (and all the buttons), and if that then outputs different values into the console for all the different power/brake notches, then there's a chance I could introduce tweaks into these patches to match the input to support your joystick. There may be a bit of tedious back/forth to get it working, but it should be doable as long as the pcsx2 controller emulation layer can get the full range of axis signal inputs. Regardless of all that, even without the USB passthrough support, I think this PR could be useful at least for players with a Zuiki, if not some other lever-like joystick controllers, so worst case I could just drop the USB passthrough patch and defer that to a subsequent PR (and maybe try to work more closely with some of you to get this emulation working with a wider range of hardware controllers). |
Oh, and naming is one of the hardest problems in computer science ;) Maybe it would help if I came up with a better name than "USB passthrough" as I agree with @mario032106 that the name is a bit misleading since it can't get raw access to the original controllers. |
As far as I understand, the denconv tool by AutoTrainTAS takes input from Windows input and then monitors for the DDG programs running and edits the memory of the running application to inject the controls into the game. Assuming that's correct, I wouldn't expect that denconv interferes with this. I could always be wrong so let me know if you observe otherwise. |
@joestringer thanks for taking your time and doing this! In my case, I'm using a (open source and simple to build) custom usb adapter to read a PS2 device (non-hid) and output as a PC One handle or Two handle device. Edit: I've adjusted the adapter to output as a Nintendo Switch controller and it's working now on the emulator (without passthrough) |
Add support for TCPP20009 controllers, datasheets courtesy of Marc Riera. Tested with a One Handle MasCon for Nintendo Switch as the controller device. Link: https://marcriera.github.io/ddgo-controller-docs/controllers/usb/tcpp20009/
Add support for TCPP20011 controllers, datasheets courtesy of Marc Riera. Tested with a One Handle MasCon for Nintendo Switch as the controller device. Link: https://marcriera.github.io/ddgo-controller-docs/controllers/usb/tcpp20011
Add support for TCPP20014 controllers, datasheets courtesy of Marc Riera. Tested with a One Handle MasCon for Nintendo Switch as the controller device. Link: https://marcriera.github.io/ddgo-controller-docs/controllers/usb/tcpp20014/
Enable an option to use native Densha De Go! controllers and pass the axis inputs from the controller directly through to the game.
@Florin9doi ah, the extra pair of eyes is much welcomed. I fixed that up and confirmed with the latest version that the unprocessed input values are passed through to the game. @sonik-br very cool! Glad to hear the Nintendo DDG mode is working for you. Hopefully now the axis passthrough mode should also work for DGOC-44U. It still won't resolve any issues if the input library can't detect the axis, but it should fix issues where PCSX2 reads the axis value and interprets it in a way that causes the throttle or brake to be "stuck" at some level and won't change when you move the axis. I've updated the branch here with the latest fixes and without the verbose logging to the console. I've also pushed a separate build here with additional logging in case anyone wants to try to figure out what's happening with the raw inputs. Builds available here: https://github.com/joestringer/pcsx2/actions . |
Still a no go for me with DGOC-44U. Power 5 shows as: |
@sonik-br OK, I've just pushed a new experimental approach to this over at joestringer#2 / https://github.com/joestringer/pcsx2/actions. With the new approach there is a specific setting for DGOC-44U which will adjust the power values based on the input you've provided earlier in this thread. You could give that a go and see if it helps. |
Tested.
|
👍 I see. Does the game recognize the notches or not? It's weird to me that notch 1 and 5 are correct but the others are off by a little. I can hack this to make it work accurate for this case, but I don't know why this is happening and whether it would help for others running the same controller. Either way I uploaded a new PR in my repo that should capture the right values hopefully. Not sure how the behaviour will be when the lever is in transition though, since the Neutral value is the same as transition in your reports. |
This fixed the power handle! |
I made a dgoc44u clone and I found that both windows and linux are reading the axes as signed int8 although the controller declares positive values and so half of values are reported as negative. And even more, Windows applies some built-in calibration and the values get shifted by one near the center. This is a draft for dgoc44u, but it should be probably applied behind a new setting; signed/unsigned mode. 0.4995f is used to compensate for calibration/rounding error, but would be better to make the axes stick to the nearest known value. switch (bind_index)
{
case CID_TC_POWER:
- s->data.power = static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
+ if (value < 0.5f)
+ s->data.power = 0x81 + static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
+ else
+ s->data.power = static_cast<u32>(std::clamp<long>(std::lroundf((value - 0.4995f) * 255.0f), 0, 255));
break;
case CID_TC_BRAKE:
- s->data.brake = static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
+ if (value < 0.5f)
+ s->data.brake = 0x81 + static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
+ else
+ s->data.brake = static_cast<u32>(std::clamp<long>(std::lroundf((value - 0.5f) * 255.0f), 0, 255));
break; |
Is this good to go for merge? |
Yes, one of the features doesn't work as intended, but it can be improved later. |
Ah right, good spotting @Florin9doi.. I wasn't taking into account that the raw values are floats and by the time my debug logging printed the values, the values were already excluding values outside the input float values of 0.0-1.0. I pushed a fresh build with this latest heuristic and a new compatibility mode setting here: joestringer#2 . If anyone wants to try that out, make sure to set the DGOC-44U compatibility mode in the controller settings. |
Still not working for me :/ Brake only shows as: And Power fron none to 5 (including transition state that shows as 1.0) : power_in=1.000000 power_out=128 type=0 compat=1 |
OK, the power_in values seem to be a bit different from what @Florin9doi was reporting with their simulated controller. Again though even accounting for the raw input from the library I can't explain the limitations in the brake inputs.. that would have to be fixed separately to get this emulation feature working with that controller. |
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/ .
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:
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