diff --git a/.gitignore b/.gitignore index b75c66b..2f6e956 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ __pycache__/ *.db -*/.vscode/ +**/.vscode/ venv/ headers/ diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 404bf77..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -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 - } - ] -} \ No newline at end of file diff --git a/README.md b/README.md index c16790c..ae86274 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,2 @@ -# Densha de GO! Controller Converter - -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**. +# ddgo-converter +Converter tool for PC versions of Densha de GO! diff --git a/ddgo-converter/gamepads/physical.py b/ddgo-converter/gamepads/physical.py index 5631a86..f45e2e0 100755 --- a/ddgo-converter/gamepads/physical.py +++ b/ddgo-converter/gamepads/physical.py @@ -1,4 +1,4 @@ -from enum import IntFlag, IntEnum, auto +from enum import IntEnum import evdev from hashlib import sha1 from events.input import InputEvent @@ -6,12 +6,8 @@ from select import select def create_gamepad(vid, pid, name): match vid, pid: - case (0x0f0d, 0x00c1) | (0x33dd, 0x0001) | (0x33dd, 0x0002): + case 0x0f0d, 0x00c1: 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) class PhysicalGamepad: @@ -20,7 +16,6 @@ class PhysicalGamepad: UNKNOWN = 0 CLASSIC = 1 SWITCH = 2 - PC = 3 def __init__(self, vid, pid, name): super().__init__() @@ -57,18 +52,15 @@ class SwitchGamepad(PhysicalGamepad): self.device = self._get_device() def start(self): - try: - self.device.grab() - except: - return + self.device.grab() def stop(self): - try: - self.device.ungrab() - except: - return + self.device.ungrab() 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) try: event = self.device.read_one() @@ -114,249 +106,34 @@ class SwitchGamepad(PhysicalGamepad): match event.value: case 0x0: # EMG input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 9)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x5: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 8)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x13: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 7)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x20: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 6)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x2E: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 5)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x3C: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 4)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x49: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 3)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x57: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 2)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x65: input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 1)) - input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x80: # N input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0)) case 0x9F: - input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 1)) case 0xB7: - input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 2)) case 0xCE: - input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 3)) case 0xE6: - input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0)) input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 4)) 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)) return input_events except OSError: diff --git a/setup.py b/setup.py index 1826740..0e105b1 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages setup(name='Densha de GO! Controller Converter', - version='1.1.0', + version='1.0.0', packages=find_packages(), )