"""Steam Input profile generation and deployment orchestration.""" from __future__ import annotations import logging from collections import OrderedDict from pathlib import Path from typing import cast from doomdeck.application.control_mapping import build_default_control_scheme from doomdeck.domain.models import Dirs, SteamInfo from doomdeck.infrastructure.files import atomic_write_text, backup_path from doomdeck.infrastructure.steam_compat import TextVDFObject, compat_mapping_key, dumps_text_vdf, load_text_vdf from doomdeck.infrastructure.steam_input_vdf import render_steam_input_layout DOOMRUNNER_STEAM_INPUT_KEY = "doom runner" STEAM_INPUT_PROFILE_FILENAME = "controller_neptune.vdf" STEAM_INPUT_CONFIGSET_FILENAME = "configset_controller_neptune.vdf" def managed_steam_input_profile_path(dirs: Dirs) -> Path: return dirs.steam_input_config / "doomrunner" / STEAM_INPUT_PROFILE_FILENAME def steam_input_profile_path(steam: SteamInfo) -> Path | None: if not steam.steam_root or not steam.user_id: return None return ( steam.steam_root / "steamapps" / "common" / "Steam Controller Configs" / steam.user_id / "config" / DOOMRUNNER_STEAM_INPUT_KEY / STEAM_INPUT_PROFILE_FILENAME ) def steam_input_configset_path(steam: SteamInfo) -> Path | None: if not steam.steam_root or not steam.user_id: return None return ( steam.steam_root / "steamapps" / "common" / "Steam Controller Configs" / steam.user_id / "config" / STEAM_INPUT_CONFIGSET_FILENAME ) def steam_input_local_override_path(steam: SteamInfo, appid: int) -> Path | None: if not steam.steam_root: return None return steam.steam_root / "controller_config" / f"app_{compat_mapping_key(appid)}.vdf" def write_managed_steam_input_profile(dirs: Dirs, dry_run: bool, logger: logging.Logger) -> Path: path = managed_steam_input_profile_path(dirs) content = render_steam_input_layout(build_default_control_scheme()) atomic_write_text(path, content, dry_run, logger) return path def deploy_steam_input_profile( dirs: Dirs, steam: SteamInfo, appid: int, dry_run: bool, logger: logging.Logger, ) -> tuple[Path, ...]: """Deploy the Doom Runner Steam Input profile to known Steam Deck config locations.""" written: list[Path] = [] profile_path = steam_input_profile_path(steam) if profile_path is not None: profile_url = f"autosave://{profile_path}" content = render_steam_input_layout(build_default_control_scheme(), url=profile_url) if _write_doomdeck_profile_if_safe(profile_path, content, dirs, dry_run, logger): written.append(profile_path) configset_path = steam_input_configset_path(steam) if configset_path is not None: _register_autosave_profile(configset_path, dirs, dry_run, logger) written.append(configset_path) override_path = steam_input_local_override_path(steam, appid) if override_path is not None: content = render_steam_input_layout(build_default_control_scheme(), url=f"autosave://{override_path}") if _write_doomdeck_profile_if_safe(override_path, content, dirs, dry_run, logger): written.append(override_path) return tuple(written) def is_doomdeck_steam_input_profile(text: str) -> bool: return "DoomDeck Hybrid KB/M" in text and "Steam Deck layout generated by DoomDeck" in text def _write_doomdeck_profile_if_safe(path: Path, content: str, dirs: Dirs, dry_run: bool, logger: logging.Logger) -> bool: if path.exists(): existing = path.read_text(encoding="utf-8", errors="ignore") if not is_doomdeck_steam_input_profile(existing): logger.warning("Preserve existing non-DoomDeck Steam Input profile: %s", path) return False backup_path(path, dirs.backups, dry_run, logger, label=path.name) atomic_write_text(path, content, dry_run, logger) return True def _register_autosave_profile(configset_path: Path, dirs: Dirs, dry_run: bool, logger: logging.Logger) -> None: if configset_path.exists(): backup_path(configset_path, dirs.backups, dry_run, logger, label=configset_path.name) root = load_text_vdf(configset_path) if "controller_config" not in root: root = cast(TextVDFObject, OrderedDict({"controller_config": OrderedDict()})) controller_config = root.get("controller_config") if not isinstance(controller_config, OrderedDict): controller_config = OrderedDict() root["controller_config"] = controller_config config = cast(TextVDFObject, controller_config) config[DOOMRUNNER_STEAM_INPUT_KEY] = OrderedDict({"autosave": "1"}) atomic_write_text(configset_path, dumps_text_vdf(root), dry_run, logger)