Created
June 30, 2023 10:50
-
-
Save Gemba/bb54d2b6fb086ae239c8add50471b001 to your computer and use it in GitHub Desktop.
Workaround to remap the Donut Dodo controller buttons to Retropie's Joypad definition.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# Remaps the Donut Dodo Controller buttons to the values of Retropie's Joypad | |
# definition. cf. https://retropie.org.uk/forum/topic/34334/ | |
# Usage: | |
# | |
# 1. Run Donut Dodo and in the "Remap Controller" Menu select Factory Reset | |
# once. This will create a | |
# /opt/retropie/configs/ports/donutdodo/controller_mapping.dat | |
# | |
# 2. Quit the game. | |
# | |
# 3. Then run this script with a *.cfg file of choice from the folder | |
# /opt/retropie/configs/all/retroarch-joypads/. E.g. | |
# | |
# ./donutdodo_controller_mapping.py "Logitech Logitech RumblePad 2 USB.cfg" | |
# Note: Donut Dodo p1_dpad_... buttons will not be changed. | |
# Tested with DonutDodo v1.39 | |
# (C) 2023 Gemba @ Github | |
# License: MIT | |
import shutil | |
import sys | |
from pathlib import Path | |
dd_to_rpjoypad_map = { | |
# Key mapping between | |
# "Donut Dodo contoller_mapping.dat": "Retropie retroarch-joypad/*.cfg" | |
# Adjust right part as needed, consult your *.cfg file for possible keys | |
'p1_jump_controller': 'input_a_btn', | |
'p1_start_controller': 'input_start_btn', | |
'p1_select_controller': 'input_select_btn', | |
'p1_options_controller': 'input_enable_hotkey_btn', | |
'p1_accept_controller': 'input_a_btn', | |
'p1_cancel_controller': 'input_b_btn' | |
} | |
RP_JOYPAD_CFG_PATH = Path("/opt/retropie/configs/all/retroarch-joypads") | |
DD_CONTROLLER_MAP_FILE = Path( | |
"/opt/retropie/configs/ports/donutdodo/controller_mapping.dat") | |
def read_rp_joypad_props(fn): | |
if fn.exists(): | |
config = {} | |
with open(fn) as props: | |
for line in [l for l in props if '=' in l]: | |
name, value = line.split('=', 1) | |
config[name.strip()] = value.strip().replace('"', '') | |
return config | |
print(f"[!] File {fn} does not exist. Exiting.") | |
sys.exit(1) | |
def replace_button_values(dd_controller, dd_key, jp_key): | |
# find controller key in *.dat file | |
idx = dd_controller.find(dd_key.encode('ascii')) | |
# button value is next byte after this sequence | |
mark = b'\x02\x00\x00\x00' | |
btn_value_idx = dd_controller.find(mark, idx)+len(mark) | |
if idx: | |
joypad_btn = int(rp_cfg[jp_key]) | |
btn_disp = dd_key.split('_')[1].upper() | |
print( | |
f" {btn_disp:7s}: {dd_controller[btn_value_idx]:2d} to" | |
f" {joypad_btn:2d} ({jp_key})") | |
new_cfg = dd_controller[:btn_value_idx] + \ | |
bytes([joypad_btn]) + dd_controller[btn_value_idx+1:] | |
return new_cfg | |
if __name__ == "__main__": | |
if len(sys.argv) != 2: | |
print( | |
"[!] Provide a RetroPie Joypad *.cfg file." | |
" See this file header for details. Exiting.") | |
sys.exit(1) | |
rp_joypad_cfg = sys.argv[1] | |
rp_joypad_file = RP_JOYPAD_CFG_PATH / rp_joypad_cfg | |
rp_cfg = read_rp_joypad_props(rp_joypad_file) | |
orig_dd_map_file = DD_CONTROLLER_MAP_FILE.parent / \ | |
f"{DD_CONTROLLER_MAP_FILE.name}.orig" | |
if not orig_dd_map_file.is_file(): | |
shutil.move(DD_CONTROLLER_MAP_FILE, orig_dd_map_file) | |
print(f"[+] Pristine mapping moved to '{orig_dd_map_file.name}'") | |
with open(orig_dd_map_file, 'rb') as in_data: | |
dd_cfg = in_data.read() | |
print(f"[*] Donut Dodo Controller Remapping to '{rp_joypad_file.stem}':") | |
for dd_controller_key, rp_joypad_key in dd_to_rpjoypad_map.items(): | |
dd_cfg = replace_button_values(dd_cfg, dd_controller_key, rp_joypad_key) | |
with open(DD_CONTROLLER_MAP_FILE, 'wb') as out_data: | |
out_data.write(dd_cfg) | |
print(f"[+] Done. New mapping written to") | |
print(f" {DD_CONTROLLER_MAP_FILE}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment