{ lib, config, pkgs, ... }: let t = v: builtins.trace v v; filterAttrs' = l: lib.filterAttrs (v: _: builtins.elem v l); cfg = config.retronix.emulationstation; attrToXml = attrs: lib.concatLines (lib.mapAttrsToList (k: v: "<${k}>${builtins.toString v}") attrs); gameTemplate = game: let attr = [ "path" "name" "sortname" "desc" "image" "video" "marquee" "thumbnail" "rating" "releasedate" "developer" "publisher" "genre" "players" "favorite" "hidden" "kidgame" "playcount" "lastplayed" ]; args = game.meta // { path = "@out@/${game.name}.sh"; }; in '' ${attrToXml (filterAttrs' attr args)} ''; systemTemplate = args: let attr = [ "name" "fullname" "path" "extension" "command" "platform" "theme" ]; in '' ${attrToXml (filterAttrs' attr args)} ''; mkSystemPath = name: games: let gamelist = '' ${lib.concatLines (builtins.map gameTemplate games)} ''; symlinkCommands = builtins.map ( v: "ln -s ${v}/${v.launchPath} $out/${v.name}.sh" ) games; in pkgs.runCommand "${name}-roms" { inherit gamelist symlinkCommands; passAsFile = [ "gamelist" ]; } '' mkdir -p $out cp $gamelistPath $out/gamelist.xml substituteAllInPlace $out/gamelist.xml ${lib.concatLines symlinkCommands} ''; sectionOptions = lib.types.submodule ({config, name, ...}: { freeformType = with lib.types; attrsOf str; options = { name = lib.mkOption { type = lib.types.str; default = name; }; fullname = lib.mkOption { type = lib.types.str; default = name; }; path = lib.mkOption { type = lib.types.path; }; command = lib.mkOption { type = lib.types.str; default = "%ROM%"; }; extension = lib.mkOption { type = lib.types.str; }; games = lib.mkOption { type = lib.types.listOf lib.types.package; description = "Takes attributes of `games` tag in gamelist.xml"; }; }; config = { path = mkSystemPath config.name config.games; }; }); in { options = { retronix = { emulationstation = { systemCfgFile = lib.mkOption { type = lib.types.path; }; inputCfgFile = lib.mkOption { type = lib.types.path; }; settingsCfgFile = lib.mkOption { type = lib.types.path; }; themesDir = lib.mkOption { type = lib.types.path; }; themes = lib.mkOption { type = lib.types.attrsOf lib.types.path; default = {}; }; inputCfg = lib.mkOption { type = lib.types.str; default = ""; }; homeDir = lib.mkOption { internal = true; type = lib.types.path; }; extraConfigFiles = lib.mkOption { type = with lib.types; listOf path; default = []; description = '' Must be store paths. Directory structure will be merged. ''; }; cli = lib.mkOption { type = lib.types.package; }; }; systems = lib.mkOption { type = lib.types.attrsOf (sectionOptions); default = {}; }; }; }; config = { retronix.emulationstation = { systemCfgFile = pkgs.writeTextDir "es_systems.cfg" '' ${lib.concatLines (lib.mapAttrsToList (_: systemTemplate) config.retronix.systems)} ''; inputCfgFile = lib.mkDefault (pkgs.writeTextDir "es_input.cfg" cfg.inputCfg); themesDir = let copyCommands = lib.concatLines (lib.mapAttrsToList (k: v: "cp -r ${v} $out/themes/${k}") cfg.themes); in pkgs.runCommand "es-themes" {} '' mkdir -p $out/themes ${copyCommands} ''; homeDir = pkgs.symlinkJoinSubdir { name = "es-config-dir"; subdir = ".emulationstation"; paths = [ cfg.systemCfgFile cfg.inputCfgFile cfg.settingsCfgFile cfg.themesDir ] ++ cfg.extraConfigFiles; }; cli = pkgs.writeShellScript "emulationstation" "${pkgs.emulationstationPatched}/bin/emulationstation --home ${cfg.homeDir} --gamelist-only"; }; }; }