Skip to content

Instantly share code, notes, and snippets.

@GiovanH
Created March 24, 2024 22:10
Show Gist options
  • Save GiovanH/c32d7e411e311de2a5db1f2b27248022 to your computer and use it in GitHub Desktop.
Save GiovanH/c32d7e411e311de2a5db1f2b27248022 to your computer and use it in GitHub Desktop.
Merges all controller mappings for the non-steam game into a combined config using layers
#!/home/deck/python-vdf/bin/python
import srctools.keyvalues
import glob
import os
import datetime
# This script merges all controller mappings for the non-steam game named SHORTCUT_NAME into a combined config using layers.
# It overwrites the bottom two grip buttons to use for paging forward and backward.
# Requires a python venv with srctools installed at ~/python-vdf:
# > python -m venv ~/python-vdf
# > ~/python-vdf/bin/python -m pip install srctools
# Settings
SHORTCUT_NAME = "lutris"
STEAM_ID = None
# Try to automatically grab STEAM_ID if unset
STEAM_ID = STEAM_ID or os.path.split(glob.glob("/home/deck/.local/share/Steam/steamapps/common/Steam Controller Configs/*")[0])[1]
config_base = f"/home/deck/.local/share/Steam/steamapps/common/Steam Controller Configs/{STEAM_ID}/config/{SHORTCUT_NAME}"
merged_name = "merged_config.vdf"
# This is just a bonus.
try:
os.symlink(
"/home/deck/.steam/steam/controller_base/templates",
f'/home/deck/.local/share/Steam/steamapps/common/Steam Controller Configs/{STEAM_ID}/config/templates'
)
except FileExistsError:
pass
blank_config = {
"controller_mappings": {
"version": "3",
"revision": "1",
"title": "Blank",
"description": "Blank layout",
"creator": "76561198043312629",
"progenitor": "",
"url": f"{config_base}/controller_neptune.vdf",
"export_type": "personal_local",
"controller_type": "controller_neptune",
"controller_caps": "23117823",
"major_revision": "0",
"minor_revision": "0",
"Timestamp": "-846098352",
"actions": {}
}
}
def dictToKV(name: str, value) -> srctools.keyvalues.Keyvalues:
if isinstance(value, dict):
return srctools.keyvalues.Keyvalues(
name,
[
dictToKV(k, v)
for k, v in value.items()
]
)
elif isinstance(value, str):
return srctools.keyvalues.Keyvalues(
name,
value
)
else:
return NotImplementedError(value, type(value))
def makeMergedConfigs(config_base):
good_configs = []
merged_config = dictToKV('root', blank_config['controller_mappings'])
merged_config['title'] = "Merged"
merged_config['description'] = "Merged config"
# Grab all configs, except the merged one
# and the default
for vdf_path in glob.glob(os.path.join(config_base, '*.vdf')):
if vdf_path.endswith(f'/{merged_name}'):
continue
if vdf_path.endswith('/controller_neptune.vdf'):
continue
good_configs.append(vdf_path)
print(good_configs)
preset_id = 0
group_id = 0
group_names = []
for config_path in good_configs:
with open(config_path, 'r') as fp:
layout = srctools.keyvalues.Keyvalues.parse(fp.read(), config_path)
layout = layout.find_key('controller_mappings')
if len([*layout.find_all('preset')]) > 1:
print(f"Multiple preset layers in {layout['title']}! Skipping")
continue
else:
print([*layout.find_all('preset')])
print("Loaded preset", layout['title'], type(layout), config_path)
layout_groups = [*layout.find_all('group')]
print(len(layout_groups), "groups") # , len([t[1] for t in layout.items() if t[0] == 'group']))
group_id_mapping = {}
for layout_group in layout_groups:
orig_id = layout_group['id']
layout_group['id'] = str(group_id)
group_id_mapping[str(orig_id)] = layout_group['id']
# print("Mapping orig", orig_id, layout_group['mode'], "to new id", group_id)
if layout_group['mode'] == "switches":
# print("Appending mode toggle overrides")
layout_group.set_key(('inputs', 'button_back_left', 'activators', 'Full_Press', 'bindings', 'binding'), "controller_action CHANGE_PRESET 32765 1 1, , ")
layout_group.set_key(('inputs', 'button_back_right', 'activators', 'Full_Press', 'bindings', 'binding'), "controller_action CHANGE_PRESET 32766 1 1, , ")
merged_config.append(layout_group)
group_id += 1
print("Done remapping groups")
layout_preset_bindings = layout.find_key('preset').find_key('group_source_bindings')
preset_key = f"Preset_100000{preset_id+1}"
if preset_id == 0:
preset_key = "Default"
block_preset = dictToKV('preset', {
"id": str(preset_id),
"name": preset_key,
"group_source_bindings": {
group_id_mapping[k]: v
for k, v in
layout_preset_bindings.as_dict().items()
}
})
merged_config.append(block_preset)
merged_config.find_key('actions').append(dictToKV(preset_key, {
"title": layout['title'],
"legacy_set": "1"
}))
print("Wrote preset and action groups")
group_names.append(layout['title'])
preset_id += 1
merged_config['description'] = f"Merged config of {', '.join(group_names)} ({datetime.datetime.now().isoformat()})"
print(merged_config['description'])
with open(os.path.join(config_base, merged_name), 'w') as fp:
kv = srctools.keyvalues.Keyvalues("controller_mappings", merged_config.value)
for line in kv.export():
fp.write(line)
makeMergedConfigs(config_base)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment