Skip to content

Instantly share code, notes, and snippets.

@Gemba
Created June 30, 2023 10:50
Show Gist options
  • Save Gemba/bb54d2b6fb086ae239c8add50471b001 to your computer and use it in GitHub Desktop.
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.
#! /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