Files
mjallen18 3c54fd5412 vibes bb
2026-03-15 19:40:42 -05:00

461 lines
16 KiB
Nix

{
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"
];
# Parser types that do not scan ROM files and therefore need no package.
platformParserTypes = [
"Epic"
"Legendary"
"GOG Galaxy"
"Amazon Games"
"UPlay"
"itch.io"
"UWP"
"EA Desktop"
"Battle.net"
"Non-SRM Shortcuts"
"Steam"
"Manual"
];
isPlatformParser = type: builtins.elem type platformParserTypes;
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 ''${<username>} 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, config, ... }:
{
options = {
enable = lib.mkEnableOption "parser for ${name}";
disabled = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Write the parser into userConfigurations.json but mark it as
disabled inside SRM (equivalent to unchecking it in the UI).
Useful for keeping a config around without having it run.
'';
};
package = lib.mkOption {
type = lib.types.nullOr lib.types.package;
# Resolve against knownEmulators, then try pkgs.<name>, then null
# for pure platform parsers that have no associated binary.
default =
if knownEmulators ? ${name} && knownEmulators.${name} ? package then
knownEmulators.${name}.package
else if isPlatformParser config.parserType then
null
else
pkgs.${name};
description = ''
Package providing the emulator binary.
Set to null for platform parsers (Epic, GOG, etc.) that do not
need a separate package managed by Nix.
'';
};
parserType = lib.mkOption {
type = lib.types.str;
default =
if knownEmulators ? ${name} && knownEmulators.${name} ? parserType then
knownEmulators.${name}.parserType
else
"Glob";
description = ''
SRM parser type. One of:
Glob, Glob-regex, Manual, Steam, Non-SRM Shortcuts,
Epic, Legendary, GOG Galaxy, Amazon Games, UPlay,
itch.io, UWP, EA Desktop, Battle.net
'';
};
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 environmentVariables.romsDirectory containing
ROMs for this emulator. Defaults to the built-in value for known
emulators; must be set explicitly for custom entries.
Leave empty for platform/artwork parsers.
'';
};
steamCategories = lib.mkOption {
type = lib.types.listOf lib.types.str;
default =
if knownEmulators ? ${name} && knownEmulators.${name} ? steamCategory then
[ knownEmulators.${name}.steamCategory ]
else
[ "" ];
description = "Steam categories / collection tags to apply to shortcuts.";
};
extraArgs = lib.mkOption {
type = lib.types.str;
default =
if knownEmulators ? ${name} && knownEmulators.${name} ? executableArgs then
knownEmulators.${name}.executableArgs
else
"\"\${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.
Leave empty for platform/artwork parsers.
'';
};
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 extraArgs to the executable path in the shortcut.";
};
# Extra parser-specific inputs (used by platform parsers and Manual).
# For Glob parsers the glob pattern is generated automatically from
# fileTypes; you do not need to set parserInputs.glob manually.
parserInputs = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default =
if knownEmulators ? ${name} && knownEmulators.${name} ? parserInputs then
knownEmulators.${name}.parserInputs
else
{ };
description = ''
Additional parser-specific input fields merged into parserInputs.
For platform parsers (Epic, Legendary, GOG, itch.io, etc.) this
carries launcher-specific settings.
For Glob parsers the "glob" key is generated automatically.
'';
};
};
}
)
);
default = { };
description = "Attribute set of emulator/parser configurations keyed by name.";
};
};
}