Files
nix-config/hosts/deck/steam-rom-manager.nix
2025-02-18 14:39:09 -06:00

321 lines
9.9 KiB
Nix

# steam-rom-manager.nix
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.steam-rom-manager;
# Common emulator configurations
commonEmulatorConfigs = {
ryujinx = {
romFolder = "switch";
binaryName = "Ryujinx";
fileTypes = [ ".nca" ".NCA" ".nro" ".NRO" ".nso" ".NSO" ".nsp" ".NSP" ".xci" ".XCI" ];
};
yuzu = {
romFolder = "switch";
binaryName = "yuzu";
fileTypes = [ ".nsp" ".NSP" ".xci" ".XCI" ];
};
pcsx2 = {
romFolder = "ps2";
binaryName = "pcsx2-qt";
fileTypes = [ ".iso" ".ISO" ".bin" ".BIN" ".chd" ".CHD" ];
};
rpcs3 = {
romFolder = "ps3";
binaryName = "rpcs3";
fileTypes = [ ".iso" ".ISO" ".bin" ".BIN" ".pkg" ".PKG" ];
};
dolphin-emu = {
romFolder = "gc";
binaryName = "dolphin-emu";
fileTypes = [ ".iso" ".ISO" ".gcm" ".GCM" ".ciso" ".CISO" ];
};
duckstation = {
romFolder = "psx";
binaryName = "duckstation-qt";
fileTypes = [ ".iso" ".ISO" ".bin" ".BIN" ".chd" ".CHD" ".pbp" ".PBP" ];
};
melonDS = {
romFolder = "nds";
binaryName = "melonDS";
fileTypes = [ ".nds" ".NDS" ];
};
cemu = {
romFolder = "wiiu";
binaryName = "cemu";
fileTypes = [ ".wud" ".WUD" ".wux" ".WUX" ".rpx" ".RPX" ];
};
};
getFileTypes = name: emu:
if (emu.fileTypes or []) != []
then emu.fileTypes
else commonEmulatorConfigs.${name}.fileTypes or [];
mkParserConfig = name: emu:
let
# We create an ordered list of key-value pairs that will maintain the exact order
# in the generated JSON output. Each field is documented for clarity.
orderedConfig = [
# Basic parser configuration
{ name = "parserType"; value = "Glob"; }
{ name = "configTitle"; value = name; }
{ name = "steamDirectory"; value = "\${steamdirglobal}"; }
{ name = "romDirectory"; value = "${cfg.romsDirectory}/${if emu.romFolder != "" then emu.romFolder else commonEmulatorConfigs.${name}.romFolder}"; }
{ name = "steamCategories"; value = [""]; }
# Executable configuration
{ name = "executableArgs"; value = emu.extraArgs; }
{ name = "executableModifier"; value = "\"\${exePath}\""; }
{ name = "startInDirectory"; value = "${cfg.romsDirectory}/${if emu.romFolder != "" then emu.romFolder else commonEmulatorConfigs.${name}.romFolder}"; }
{ name = "titleModifier"; value = "\${fuzzyTitle}"; }
# Controller settings
{ name = "fetchControllerTemplatesButton"; value = null; }
{ name = "removeControllersButton"; value = null; }
{ name = "steamInputEnabled"; value = "1"; }
# Image provider configuration
{ name = "imageProviders"; value = [ "sgdb" ]; }
{ name = "onlineImageQueries"; value = [ "\${fuzzyTitle}" ]; }
{ name = "imagePool"; value = "\${fuzzyTitle}"; }
# DRM and user account settings
{ name = "drmProtect"; value = false; }
{ name = "userAccounts"; value = {
specifiedAccounts = [ "Global" ];
}; }
# Parser-specific settings
{ name = "parserInputs"; value = {
glob = "\${title}@(${concatStringsSep "|" (getFileTypes name emu)})";
}; }
# Executable details
{ name = "executable"; value = {
path = "${emu.package}/bin/${if emu.binaryName != "" then emu.binaryName else commonEmulatorConfigs.${name}.binaryName}";
shortcutPassthrough = false;
appendArgsToExecutable = true;
}; }
# Title and fuzzy matching configuration
{ name = "titleFromVariable"; value = {
limitToGroups = [];
caseInsensitiveVariables = false;
skipFileIfVariableWasNotFound = false;
}; }
{ name = "fuzzyMatch"; value = {
replaceDiacritics = true;
removeCharacters = true;
removeBrackets = true;
}; }
# Controller configuration
{ 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;
}; }
# Image provider API configuration
{ name = "imageProviderAPIs"; value = {
sgdb = {
nsfw = false;
humor = false;
styles = [];
stylesHero = [];
stylesLogo = [];
stylesIcon = [];
imageMotionTypes = [ "static" ];
sizes = [];
sizesHero = [];
sizesTall = null;
sizesIcon = [];
};
}; }
# Default and local image settings
{ name = "defaultImage"; value = {
tall = "";
long = "";
hero = "";
logo = "";
icon = "";
}; }
{ name = "localImages"; value = {
tall = "";
long = "";
hero = "";
logo = "";
icon = "";
}; }
# Parser identification
{ name = "parserId"; value = name; }
{ name = "version"; value = 25; }
];
# Function to convert our ordered list into properly formatted JSON
makeOrderedJSON = pairs:
let
# Create JSON key-value pairs while preserving order
joined = builtins.concatStringsSep ","
(map (pair: "\"${pair.name}\":${builtins.toJSON pair.value}") pairs);
in
"{${joined}}";
in
makeOrderedJSON orderedConfig;
# Create AppImage package
steam-rom-manager = pkgs.fetchurl {
name = "steam-rom-manager-2.5.29.AppImage";
url = "https://github.com/SteamGridDB/steam-rom-manager/releases/download/v2.5.29/Steam-ROM-Manager-2.5.29.AppImage";
hash = "sha256-6ZJ+MGIgr2osuQuqD6N9NnPiJFNq/HW6ivG8tyXUhvs=";
};
in {
options.programs.steam-rom-manager = {
enable = mkEnableOption "Steam ROM Manager";
package = mkOption {
type = types.package;
default = steam-rom-manager;
description = "Steam ROM Manager package";
};
steamUsername = mkOption {
type = types.str;
description = "Steam username for configuration";
};
romsDirectory = mkOption {
type = types.str;
default = "${config.home.homeDirectory}/Emulation/roms";
description = "Base directory for ROM files";
};
steamDirectory = mkOption {
type = types.str;
default = "${config.home.homeDirectory}/.local/share/Steam";
description = "Steam installation directory";
};
emulators = mkOption {
type = types.attrsOf (types.submodule {
options = {
enable = mkEnableOption "emulator configuration";
package = mkOption {
type = types.package;
description = "Emulator package";
};
binaryName = mkOption {
type = types.str;
default = "";
description = "Name of the emulator binary (defaults to common configuration)";
};
romFolder = mkOption {
type = types.str;
default = "";
description = "Name of the ROM folder (defaults to common configuration)";
};
fileTypes = mkOption {
type = types.listOf types.str;
default = [];
description = "List of ROM file types (defaults to common configuration)";
};
extraArgs = mkOption {
type = types.str;
default = "--fullscreen \"\${filePath}\"";
description = "Additional emulator arguments";
};
};
});
default = {};
description = "Emulator configurations";
};
};
config = mkIf cfg.enable {
home.packages = [ pkgs.appimage-run ]
++ mapAttrsToList (_: v: v.package) (filterAttrs (_: v: v.enable) cfg.emulators);
xdg.desktopEntries.steam-rom-manager = {
name = "Steam ROM Manager";
exec = "${pkgs.appimage-run}/bin/appimage-run ${cfg.package}";
icon = "steam";
categories = [ "Game" "Utility" ];
type = "Application";
};
# Configuration files using xdg.configFile
xdg.configFile = {
# User settings configuration
"steam-rom-manager/userData/userSettings.json".text = builtins.toJSON {
fuzzyMatcher = {
timestamps = {
check = 0;
download = 0;
};
verbose = false;
filterProviders = true;
};
environmentVariables = {
steamDirectory = cfg.steamDirectory;
userAccounts = "\${${cfg.steamUsername}}";
romsDirectory = cfg.romsDirectory;
retroarchPath = "";
raCoresDirectory = "";
localImagesDirectory = "";
};
previewSettings = {
retrieveCurrentSteamImages = true;
disableCategories = false;
deleteDisabledShortcuts = false;
imageZoomPercentage = 30;
preload = false;
hideUserAccount = false;
};
enabledProviders = [ "sgdb" "steamCDN" ];
batchDownloadSize = 50;
dnsServers = [];
language = "en-US";
theme = "Deck";
emudeckInstall = false;
autoUpdate = false;
offlineMode = false;
navigationWidth = 0;
clearLogOnTest = true;
version = 8;
};
# Parser configurations
"steam-rom-manager/userData/userConfigurations.json".text =
let
# Generate configurations
configs = mapAttrsToList (name: emu:
mkParserConfig name (emu // {
romFolder = if emu.romFolder != "" then emu.romFolder else commonEmulatorConfigs.${name}.romFolder;
binaryName = if emu.binaryName != "" then emu.binaryName else commonEmulatorConfigs.${name}.binaryName;
})
) (filterAttrs (_: v: v.enable) cfg.emulators);
# Join configurations into a valid JSON array
configsJson = "[${concatStringsSep "," configs}]";
in
configsJson;
};
};
}