{ config, lib, pkgs, ... }: let cfg = config.programs.steam-rom-manager; # Single source of truth for built-in emulator metadata. knownEmulators = import ./emulators.nix pkgs; # Valid image provider identifiers accepted by SRM. validProviders = [ "sgdb" "steamCDN" ]; in { options.programs.steam-rom-manager = { enable = lib.mkEnableOption "Steam ROM Manager"; # ------------------------------------------------------------------------- # Fuzzy matcher # ------------------------------------------------------------------------- fuzzyMatcher = { timestamps = { check = lib.mkOption { type = lib.types.int; default = 0; description = "Epoch timestamp of the last fuzzy-matcher data check."; }; download = lib.mkOption { type = lib.types.int; default = 0; description = "Epoch timestamp of the last fuzzy-matcher data download."; }; }; verbose = lib.mkOption { type = lib.types.bool; default = false; description = "Enable verbose logging for the fuzzy matcher."; }; filterProviders = lib.mkOption { type = lib.types.bool; default = true; description = "Restrict image lookups to the configured providers."; }; }; # ------------------------------------------------------------------------- # Environment variables written into userSettings.json # ------------------------------------------------------------------------- environmentVariables = { steamDirectory = lib.mkOption { type = lib.types.str; default = "${config.home.homeDirectory}/.local/share/Steam"; description = "Path to the Steam data directory."; }; romsDirectory = lib.mkOption { type = lib.types.str; default = "${config.home.homeDirectory}/Emulation/roms"; description = "Root directory that contains per-system ROM sub-folders."; }; retroarchPath = lib.mkOption { type = lib.types.str; default = ""; description = "Path to the RetroArch executable (leave empty if not using RetroArch)."; }; raCoresDirectory = lib.mkOption { type = lib.types.str; default = ""; description = "Directory containing RetroArch cores."; }; localImagesDirectory = lib.mkOption { type = lib.types.str; default = ""; description = "Directory for locally stored artwork."; }; }; # ------------------------------------------------------------------------- # Preview settings # ------------------------------------------------------------------------- previewSettings = { retrieveCurrentSteamImages = lib.mkOption { type = lib.types.bool; default = true; description = "Download existing Steam artwork when previewing."; }; disableCategories = lib.mkOption { type = lib.types.bool; default = false; description = "Do not apply Steam category tags to shortcuts."; }; deleteDisabledShortcuts = lib.mkOption { type = lib.types.bool; default = false; description = "Remove shortcuts for parsers that are disabled."; }; imageZoomPercentage = lib.mkOption { type = lib.types.int; default = 30; description = "Zoom level (%) used when displaying artwork in the preview pane."; }; preload = lib.mkOption { type = lib.types.bool; default = false; description = "Pre-fetch artwork while the preview loads."; }; hideUserAccount = lib.mkOption { type = lib.types.bool; default = false; description = "Hide the Steam account name in the preview pane."; }; }; # ------------------------------------------------------------------------- # Image providers # ------------------------------------------------------------------------- enabledProviders = lib.mkOption { type = lib.types.listOf (lib.types.enum validProviders); default = [ "sgdb" "steamCDN" ]; description = '' Ordered list of image providers SRM should query. Valid values: ${lib.concatStringsSep ", " (map (p: ''"${p}"'') validProviders)}. ''; }; imageProviderSettings = { sgdb = { nsfw = lib.mkOption { type = lib.types.bool; default = false; description = "Include NSFW artwork from SteamGridDB."; }; humor = lib.mkOption { type = lib.types.bool; default = false; description = "Include humour-tagged artwork from SteamGridDB."; }; styles = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Preferred grid artwork styles (e.g. \"alternate\", \"blurred\")."; }; stylesHero = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Preferred hero artwork styles."; }; stylesLogo = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Preferred logo styles."; }; stylesIcon = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Preferred icon styles."; }; imageMotionTypes = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "static" ]; description = "Allowed motion types (\"static\", \"animated\")."; }; sizes = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Preferred grid image sizes."; }; sizesHero = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Preferred hero image sizes."; }; sizesIcon = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Preferred icon sizes."; }; }; }; # ------------------------------------------------------------------------- # Miscellaneous top-level settings # ------------------------------------------------------------------------- batchDownloadSize = lib.mkOption { type = lib.types.int; default = 50; description = "Number of images to download per batch."; }; dnsServers = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "Custom DNS servers used for artwork downloads (leave empty for system DNS)."; }; language = lib.mkOption { type = lib.types.str; default = "en-US"; example = "de-DE"; description = "BCP-47 language tag for the SRM UI."; }; theme = lib.mkOption { type = lib.types.str; default = "Deck"; example = "Default"; description = "SRM UI theme name."; }; emudeckInstall = lib.mkOption { type = lib.types.bool; default = false; description = "Set to true when SRM is managed alongside EmuDeck."; }; autoUpdate = lib.mkOption { type = lib.types.bool; default = false; description = "Allow SRM to update itself automatically (not recommended in a Nix-managed setup)."; }; offlineMode = lib.mkOption { type = lib.types.bool; default = false; description = "Run SRM without fetching remote artwork."; }; navigationWidth = lib.mkOption { type = lib.types.int; default = 0; description = "Width in pixels of the navigation sidebar (0 = default)."; }; clearLogOnTest = lib.mkOption { type = lib.types.bool; default = true; description = "Clear the log panel each time a parser test is run."; }; steamUsername = lib.mkOption { type = lib.types.str; example = "john"; description = '' Steam account username used to build the SRM environment-variable reference ''${} in userSettings.json. This must match the account name shown in Steam (not the display name). ''; }; # ------------------------------------------------------------------------- # Emulator parser configurations # ------------------------------------------------------------------------- emulators = lib.mkOption { type = lib.types.attrsOf ( lib.types.submodule ( { name, ... }: { options = { enable = lib.mkEnableOption "emulator parser for ${name}"; package = lib.mkOption { type = lib.types.package; # Resolve against knownEmulators first; fall back to pkgs. so # users can supply any arbitrary emulator key without a code change. default = if knownEmulators ? ${name} then knownEmulators.${name}.package else pkgs.${name}; description = "Package providing the emulator binary."; }; parserType = lib.mkOption { type = lib.types.str; default = if knownEmulators ? ${name} && knownEmulators.${name} ? parserType then knownEmulators.${name}.parserType else "Glob"; description = "SRM parser type (usually \"Glob\" for file-based ROMs)."; }; configTitle = lib.mkOption { type = lib.types.str; default = name; description = "Human-readable label shown inside SRM for this parser."; }; romFolder = lib.mkOption { type = lib.types.str; default = ""; description = '' Sub-folder under that contains ROMs for this emulator. Defaults to the built-in value for known emulators; must be set explicitly for custom ones. ''; }; steamCategories = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "" ]; description = "Steam categories / collection tags to apply to shortcuts."; }; extraArgs = lib.mkOption { type = lib.types.str; default = "--fullscreen \"\${filePath}\""; description = "Command-line arguments passed to the emulator."; }; executableModifier = lib.mkOption { type = lib.types.str; default = "\"\${exePath}\""; description = "SRM executable modifier expression."; }; titleModifier = lib.mkOption { type = lib.types.str; default = "\${fuzzyTitle}"; description = "SRM title modifier expression."; }; steamInputEnabled = lib.mkOption { type = lib.types.bool; default = false; description = "Enable Steam Input controller remapping for shortcuts."; }; onlineImageQueries = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "\${fuzzyTitle}" ]; description = "Query strings used to search for artwork online."; }; imagePool = lib.mkOption { type = lib.types.str; default = "\${fuzzyTitle}"; description = "SRM image pool identifier for artwork caching."; }; drmProtected = lib.mkOption { type = lib.types.bool; default = false; description = "Mark shortcuts as DRM-protected."; }; userAccounts = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "Global" ]; description = "Steam accounts to apply this parser to (\"Global\" applies to all)."; }; fileTypes = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = '' File extensions matched by the glob parser (e.g. ".iso"). Defaults to the built-in list for known emulators; must be set for custom ones. ''; }; shortcutPassthrough = lib.mkOption { type = lib.types.bool; default = false; description = "Pass through existing Steam shortcuts rather than replacing them."; }; appendArgsToExecutable = lib.mkOption { type = lib.types.bool; default = true; description = "Append to the executable path in the shortcut."; }; }; } ) ); default = { }; description = "Attribute set of emulator parser configurations keyed by emulator name."; }; }; }