Files
nix-steam-rom-manager/modules/steam-rom-manager/default.nix
mjallen18 2fbb4d1f1b upd
2026-03-15 19:30:48 -05:00

317 lines
9.5 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
version = "2.5.34";
cfg = config.programs.steam-rom-manager;
# Single source of truth for built-in emulator metadata (package, romFolder,
# fileTypes). options.nix imports the same file so both files stay in sync.
knownEmulators = import ./emulators.nix pkgs;
# ---------------------------------------------------------------------------
# Build an ordered JSON object for one parser entry.
# SRM requires keys in a specific order; builtins.toJSON does not guarantee
# order, so we construct the JSON string manually from an ordered list.
#
# Schema version 25 corresponds to SRM ≥ 2.5.x. If you upgrade SRM and the
# config format changes, bump `version` here and audit the field list.
# ---------------------------------------------------------------------------
mkParserConfig =
name: emu:
let
known = knownEmulators.${name} or null;
romFolder =
if emu.romFolder != "" then
emu.romFolder
else if known != null then
known.romFolder
else
lib.warn "steam-rom-manager: unknown emulator '${name}', romFolder not set" "";
fileTypes =
if emu.fileTypes != [ ] then
emu.fileTypes
else if known != null then
known.fileTypes
else
lib.warn "steam-rom-manager: unknown emulator '${name}', fileTypes not set" [ ];
parserType = emu.parserType;
# lib.getExe resolves the primary binary without IFD path probing.
exePath = lib.getExe emu.package;
orderedConfig = [
{
name = "parserType";
value = parserType;
}
{
name = "configTitle";
value = emu.configTitle;
}
{
name = "steamDirectory";
value = "\${steamdirglobal}";
}
{
name = "romDirectory";
value = "${cfg.environmentVariables.romsDirectory}/${romFolder}";
}
{
name = "steamCategories";
value = emu.steamCategories;
}
{
name = "executableArgs";
value = emu.extraArgs;
}
{
name = "executableModifier";
value = emu.executableModifier;
}
{
name = "startInDirectory";
value = "${cfg.environmentVariables.romsDirectory}/${romFolder}";
}
{
name = "titleModifier";
value = emu.titleModifier;
}
# fetchControllerTemplatesButton / removeControllersButton are UI-only
# action triggers in SRM; they have no meaningful config value and must
# be present as null for schema compatibility.
{
name = "fetchControllerTemplatesButton";
value = null;
}
{
name = "removeControllersButton";
value = null;
}
{
name = "steamInputEnabled";
value = if emu.steamInputEnabled then "1" else "0";
}
{
name = "imageProviders";
value = cfg.enabledProviders;
}
{
name = "onlineImageQueries";
value = emu.onlineImageQueries;
}
{
name = "imagePool";
value = emu.imagePool;
}
{
name = "drmProtect";
value = emu.drmProtected;
}
{
name = "userAccounts";
value = {
specifiedAccounts = emu.userAccounts;
};
}
{
name = "parserInputs";
value = {
glob = "\${title}@(${lib.concatStringsSep "|" fileTypes})";
};
}
{
name = "executable";
value = {
path = exePath;
shortcutPassthrough = emu.shortcutPassthrough;
appendArgsToExecutable = emu.appendArgsToExecutable;
};
}
{
name = "titleFromVariable";
value = {
limitToGroups = [ ];
caseInsensitiveVariables = false;
skipFileIfVariableWasNotFound = false;
};
}
{
name = "fuzzyMatch";
value = {
replaceDiacritics = true;
removeCharacters = true;
removeBrackets = true;
};
}
{
name = "controllers";
value = {
ps4 = null;
ps5 = null;
ps5_edge = null;
xbox360 = null;
xboxone = null;
xboxelite = null;
switch_joycon_left = null;
switch_joycon_right = null;
switch_pro = null;
neptune = null;
steamcontroller_gordon = null;
};
}
{
name = "imageProviderAPIs";
value = {
sgdb = cfg.imageProviderSettings.sgdb;
};
}
{
name = "defaultImage";
value = {
tall = "";
long = "";
hero = "";
logo = "";
icon = "";
};
}
{
name = "localImages";
value = {
tall = "";
long = "";
hero = "";
logo = "";
icon = "";
};
}
{
name = "parserId";
value = name;
}
# SRM userConfigurations schema version — bump when upgrading past a
# breaking config format change in SRM itself.
{
name = "version";
value = 25;
}
];
makeOrderedJSON =
pairs:
let
joined = builtins.concatStringsSep "," (
map (pair: "\"${pair.name}\":${builtins.toJSON pair.value}") pairs
);
in
"{${joined}}";
in
makeOrderedJSON orderedConfig;
# ---------------------------------------------------------------------------
# Icon — pinned to the commit that introduced the current SVG so the hash
# does not break when unrelated files change on master.
# Commit: 1670cbe8871a632708e0440ccef52c5c5c403ddc (2024-01-12)
# ---------------------------------------------------------------------------
steam-rom-manager-icon = pkgs.fetchurl {
name = "steam-rom-manager.svg";
url = "https://raw.githubusercontent.com/SteamGridDB/steam-rom-manager/1670cbe8871a632708e0440ccef52c5c5c403ddc/src/assets/icons/steam-rom-manager.svg";
hash = "sha256-DKzNIs5UhIWAVRTfinvCb8WqeDniPWw9Z08/p/Zpa9E=";
};
# ---------------------------------------------------------------------------
# Wrap the upstream AppImage with appimage-run.
# ---------------------------------------------------------------------------
steam-rom-manager-appimage = pkgs.writeShellScriptBin "steam-rom-manager" ''
exec ${pkgs.appimage-run}/bin/appimage-run ${
pkgs.fetchurl {
name = "steam-rom-manager-${version}.AppImage";
url = "https://github.com/SteamGridDB/steam-rom-manager/releases/download/v${version}/Steam-ROM-Manager-${version}.AppImage";
hash = "sha256-QbXwfT91BQ15/DL3IYC3qZcahlsQvkUKTwMUUpZY+U8=";
}
} "$@"
'';
in
{
imports = [ ./options.nix ];
config = lib.mkIf cfg.enable {
home.packages = [
pkgs.appimage-run
steam-rom-manager-appimage
]
++ lib.mapAttrsToList (_: v: v.package) (lib.filterAttrs (_: v: v.enable) cfg.emulators);
xdg.dataFile."icons/hicolor/scalable/apps/steam-rom-manager.svg".source = steam-rom-manager-icon;
xdg.desktopEntries.steam-rom-manager = {
name = "Steam ROM Manager";
exec = "${steam-rom-manager-appimage}/bin/steam-rom-manager";
icon = "steam-rom-manager";
categories = [
"Game"
"Utility"
];
type = "Application";
terminal = false;
comment = "Add ROMs to Steam with artwork";
settings = {
"X-KDE-StartupNotify" = "true";
"X-KDE-SubstituteUID" = "false";
"X-DBUS-StartupType" = "Unique";
};
};
xdg.configFile = {
# userSettings schema version 8 corresponds to SRM ≥ 2.4.x.
"steam-rom-manager/userData/userSettings.json".text = builtins.toJSON {
fuzzyMatcher = {
timestamps = { inherit (cfg.fuzzyMatcher.timestamps) check download; };
verbose = cfg.fuzzyMatcher.verbose;
filterProviders = cfg.fuzzyMatcher.filterProviders;
};
environmentVariables = {
steamDirectory = cfg.environmentVariables.steamDirectory;
userAccounts = "\${${cfg.steamUsername}}";
romsDirectory = cfg.environmentVariables.romsDirectory;
retroarchPath = cfg.environmentVariables.retroarchPath;
raCoresDirectory = cfg.environmentVariables.raCoresDirectory;
localImagesDirectory = cfg.environmentVariables.localImagesDirectory;
};
previewSettings = cfg.previewSettings;
enabledProviders = cfg.enabledProviders;
imageProviderAPIs = {
sgdb = cfg.imageProviderSettings.sgdb;
};
batchDownloadSize = cfg.batchDownloadSize;
dnsServers = cfg.dnsServers;
language = cfg.language;
theme = cfg.theme;
emudeckInstall = cfg.emudeckInstall;
autoUpdate = cfg.autoUpdate;
offlineMode = cfg.offlineMode;
navigationWidth = cfg.navigationWidth;
clearLogOnTest = cfg.clearLogOnTest;
version = 8;
};
"steam-rom-manager/userData/userConfigurations.json".text =
let
configs = lib.mapAttrsToList (name: emu: mkParserConfig name emu) (
lib.filterAttrs (_: v: v.enable) cfg.emulators
);
in
"[${lib.concatStringsSep "," configs}]";
};
};
}