Compare commits

..

No commits in common. "main" and "1.0.0" have entirely different histories.
main ... 1.0.0

5 changed files with 11 additions and 282 deletions

2
.gitignore vendored
View file

@ -1,5 +1,5 @@
__pycache__/ __pycache__/
*.db *.db
*/.vscode/ **/.vscode/
venv/ venv/
headers/ headers/

16
.vscode/launch.json vendored
View file

@ -1,16 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run main program",
"type": "python",
"request": "launch",
"program": "ddgo-converter/ddgo-converter.py",
"console": "integratedTerminal",
"justMyCode": true
}
]
}

View file

@ -1,34 +1,2 @@
# Densha de GO! Controller Converter # ddgo-converter
Converter tool for PC versions of Densha de GO!
This tool allows using a physical _Densha de GO!_ controller with a game that does not officially support it. It is Linux only (check https://autotraintas.hariko.com/ if you use Windows).
## How it works
The program reads input from a real controller, translates it and sends it to an emulated controller, which is picked up by the game.
## Installation
The executable is ready to use. However, you will need read AND write permissions on `/dev/uinput` for the program to work.
**NOTE:** Currently, this fails to launch on the Steam Deck in Gaming Mode. It works fine in Desktop Mode, but you need to disable Steam Input (or close Steam entirely).
## Supported controllers
### Physical (input)
- One-handle controller for PC (DGC-255)
- Two-handle controller for PC (DGOC-44U)
- One-handle controller for Nintendo Switch (ZKNS-001)
### Emulated (output)
- Two-handle controller for PC (DGOC-44U)
- Two-handle controller for Sony PlayStation (SLPH-00051)
- Two-handle controller for Nintendo 64 (TCPP-20003)
- Two-handle controller for SEGA Saturn (TC-5175290)
## Notes
When emulating console controllers, an emulated Sony PlayStation 3 controller is used for easier mapping. On RetroArch, everything should work out of the box.
_Densha de GO! 64_ requires connecting the controller to Port 3 and enabling **Independent C-button Controls**.

View file

@ -1,4 +1,4 @@
from enum import IntFlag, IntEnum, auto from enum import IntEnum
import evdev import evdev
from hashlib import sha1 from hashlib import sha1
from events.input import InputEvent from events.input import InputEvent
@ -6,12 +6,8 @@ from select import select
def create_gamepad(vid, pid, name): def create_gamepad(vid, pid, name):
match vid, pid: match vid, pid:
case (0x0f0d, 0x00c1) | (0x33dd, 0x0001) | (0x33dd, 0x0002): case 0x0f0d, 0x00c1:
return SwitchGamepad(vid, pid, name) return SwitchGamepad(vid, pid, name)
case 0x054c, 0x0268:
return ClassicGamepad(vid, pid, name)
case 0x0ae4, 0x0003:
return PCGamepad(vid, pid, name)
return PhysicalGamepad(vid, pid, name) return PhysicalGamepad(vid, pid, name)
class PhysicalGamepad: class PhysicalGamepad:
@ -20,7 +16,6 @@ class PhysicalGamepad:
UNKNOWN = 0 UNKNOWN = 0
CLASSIC = 1 CLASSIC = 1
SWITCH = 2 SWITCH = 2
PC = 3
def __init__(self, vid, pid, name): def __init__(self, vid, pid, name):
super().__init__() super().__init__()
@ -57,18 +52,15 @@ class SwitchGamepad(PhysicalGamepad):
self.device = self._get_device() self.device = self._get_device()
def start(self): def start(self):
try: self.device.grab()
self.device.grab()
except:
return
def stop(self): def stop(self):
try: self.device.ungrab()
self.device.ungrab()
except:
return
def read_input(self): def read_input(self):
# time.sleep(5)
# print("Read from ZKNS-001 correct")
# return InputEvent(InputEvent.EventType.PRESS_BUTTON, InputEvent.Button.BUTTON_A)
select([self.device], [], [], 5) select([self.device], [], [], 5)
try: try:
event = self.device.read_one() event = self.device.read_one()
@ -114,249 +106,34 @@ class SwitchGamepad(PhysicalGamepad):
match event.value: match event.value:
case 0x0: # EMG case 0x0: # EMG
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 9)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 9))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x5: case 0x5:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 8)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 8))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x13: case 0x13:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 7)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 7))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x20: case 0x20:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 6)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 6))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x2E: case 0x2E:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 5)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 5))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x3C: case 0x3C:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 4)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 4))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x49: case 0x49:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 3)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 3))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x57: case 0x57:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 2)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 2))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x65: case 0x65:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 1)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 1))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x80: # N case 0x80: # N
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0)) input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x9F: case 0x9F:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 1)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 1))
case 0xB7: case 0xB7:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 2)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 2))
case 0xCE: case 0xCE:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 3)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 3))
case 0xE6: case 0xE6:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 4)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 4))
case 0xFF: # P5 case 0xFF: # P5
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 5))
return input_events
except OSError:
return [InputEvent(InputEvent.EventType.ERROR, None)]
class ClassicGamepad(PhysicalGamepad):
class ButtonType(IntEnum):
BUTTON = 0
POWER = 1
BRAKE = 2
class Buttons(IntFlag):
BUTTON_A = auto()
BUTTON_B = auto()
BUTTON_C = auto()
BUTTON_SELECT = auto()
BUTTON_START = auto()
class Power(IntFlag):
POWER1 = auto()
POWER2 = auto()
POWER3 = auto()
class Brake(IntFlag):
BRAKE1 = auto()
BRAKE2 = auto()
BRAKE3 = auto()
BRAKE4 = auto()
class ButtonConfig():
def __init__(self, type, code, button):
self.type = type
self.code = code
self.button = button
def __init__(self, * args):
super().__init__(* args)
self.type = self.GamepadType.CLASSIC
self.config = [ self.ButtonConfig(self.ButtonType.BUTTON, 308, self.Buttons.BUTTON_A), self.ButtonConfig(self.ButtonType.BUTTON, 304, self.Buttons.BUTTON_B), self.ButtonConfig(self.ButtonType.BUTTON, 305, self.Buttons.BUTTON_C), self.ButtonConfig(self.ButtonType.BUTTON, 314, self.Buttons.BUTTON_SELECT), self.ButtonConfig(self.ButtonType.BUTTON, 315, self.Buttons.BUTTON_START),
self.ButtonConfig(self.ButtonType.POWER, 307, self.Power.POWER1), self.ButtonConfig(self.ButtonType.POWER, 546, self.Power.POWER2), self.ButtonConfig(self.ButtonType.POWER, 547, self.Power.POWER3),
self.ButtonConfig(self.ButtonType.BRAKE, 310, self.Brake.BRAKE1), self.ButtonConfig(self.ButtonType.BRAKE, 312, self.Brake.BRAKE2), self.ButtonConfig(self.ButtonType.BRAKE, 311, self.Brake.BRAKE3), self.ButtonConfig(self.ButtonType.BRAKE, 313, self.Brake.BRAKE4) ]
self.device = self._get_device()
self.buttons = IntFlag(0)
self.power = IntFlag(0)
self.brake = IntFlag(0)
def start(self):
try:
self.device.grab()
except:
return
def stop(self):
try:
self.device.ungrab()
except:
return
def read_input(self):
select([self.device], [], [], 5)
try:
event = self.device.read_one()
input_events = []
if event is not None:
for button in self.config:
match button.type:
case self.ButtonType.POWER:
if event.type == evdev.ecodes.EV_KEY and event.code == button.code and event.value == 0:
self.power &= ~button.button
if event.type == evdev.ecodes.EV_KEY and event.code == button.code and event.value == 1:
self.power |= button.button
case self.ButtonType.BRAKE:
if event.type == evdev.ecodes.EV_KEY and event.code == button.code and event.value == 0:
self.brake &= ~button.button
if event.type == evdev.ecodes.EV_KEY and event.code == button.code and event.value == 1:
self.brake |= button.button
case self.ButtonType.BUTTON:
if event.type == evdev.ecodes.EV_KEY and event.code == button.code and event.value == 0:
self.buttons &= ~button.button
if event.type == evdev.ecodes.EV_KEY and event.code == button.code and event.value == 1:
self.buttons |= button.button
input_events.append(InputEvent(InputEvent.EventType(self.Buttons.BUTTON_A in self.buttons), InputEvent.Button.BUTTON_A))
input_events.append(InputEvent(InputEvent.EventType(self.Buttons.BUTTON_B in self.buttons), InputEvent.Button.BUTTON_B))
input_events.append(InputEvent(InputEvent.EventType(self.Buttons.BUTTON_C in self.buttons), InputEvent.Button.BUTTON_C))
input_events.append(InputEvent(InputEvent.EventType(self.Buttons.BUTTON_SELECT in self.buttons), InputEvent.Button.BUTTON_SELECT))
input_events.append(InputEvent(InputEvent.EventType(self.Buttons.BUTTON_START in self.buttons), InputEvent.Button.BUTTON_START))
if event.type == evdev.ecodes.EV_SYN:
match self.power:
case 6:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 5:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 1))
case 4:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 2))
case 3:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 3))
case 2:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 4))
case 1:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 5))
match self.brake:
case 14:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
case 13:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 1))
case 12:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 2))
case 11:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 3))
case 10:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 4))
case 9:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 5))
case 8:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 6))
case 7:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 7))
case 6:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 8))
case 0:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 9))
return input_events
except OSError:
return [InputEvent(InputEvent.EventType.ERROR, None)]
class PCGamepad(PhysicalGamepad):
def __init__(self, * args):
super().__init__(* args)
self.type = self.GamepadType.PC
self.config = []
self.device = self._get_device()
def start(self):
try:
self.device.grab()
except:
return
def stop(self):
try:
self.device.ungrab()
except:
return
def read_input(self):
select([self.device], [], [], 5)
try:
event = self.device.read_one()
input_events = []
if event is not None:
if event.type == evdev.ecodes.EV_KEY:
match event.code:
case 289:
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_A))
case 288:
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_B))
case 290:
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_C))
case 291:
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_D))
case 292:
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_SELECT))
case 293:
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_START))
if event.type == evdev.ecodes.EV_ABS and event.code == evdev.ecodes.ABS_X:
match event.value:
case 0xB9: # EMG
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 9))
case 0xB5:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 8))
case 0xB2:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 7))
case 0xAF:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 6))
case 0xA8:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 5))
case 0xA2:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 4))
case 0x9A:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 3))
case 0x94:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 2))
case 0x8A:
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 1))
case 0x79: # N
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
if event.type == evdev.ecodes.EV_ABS and event.code == evdev.ecodes.ABS_Y:
match event.value:
case 0x81: # N
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
case 0x6D:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 1))
case 0x54:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 2))
case 0x3F:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 3))
case 0x21:
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 4))
case 0x00: # P5
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 5)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 5))
return input_events return input_events
except OSError: except OSError:

View file

@ -1,6 +1,6 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
setup(name='Densha de GO! Controller Converter', setup(name='Densha de GO! Controller Converter',
version='1.1.0', version='1.0.0',
packages=find_packages(), packages=find_packages(),
) )