infrastructure/modules/nixos/steam/beatsaber.nix

878 lines
27 KiB
Nix

{
config,
pkgs,
lib,
inputs,
...
}: let
inherit (inputs.self.lib.lib) userIs;
inherit (config.lib.steam) mkSharePath;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.strings) hasSuffix replaceStrings optionalString concatStringsSep escapeShellArg makeBinPath versionOlder;
inherit (lib.attrsets) attrValues filterAttrs mapAttrs mapAttrs' mapAttrsToList listToAttrs nameValuePair;
inherit (lib.lists) concatMap head last filter sort singleton;
inherit (config.services.steam) accountSwitch;
cfg = config.services.steam.beatsaber;
sortedVersions = sort (a: b: versionOlder a.version b.version) (attrValues cfg.versions);
prevVersionFor = version: let
olderVersions = filter (v: versionOlder v.version version) sortedVersions;
in if olderVersions != [] then last olderVersions else null;
versionModule = { config, name, ... }: {
options = with lib.types; {
version = mkOption {
type = str;
default = name;
};
previousVersion = mkOption {
type = nullOr str;
};
__toString = mkOption {
type = functionTo str;
};
};
config = {
__toString = mkOptionDefault (
config: config.version
);
previousVersion = mkOptionDefault (
prevVersionFor config.version
);
};
};
fileModule = { config, name, ... }: {
options = with lib.types; {
relativePath = mkOption {
type = str;
default = name;
};
type = mkOption {
type = enum [ "file" "directory" ];
default = "file";
};
versioned = mkOption {
type = bool;
default = false;
};
target = mkOption {
type = enum [ "user" "shared" "game" ];
default = "user";
};
mode = {
file = mkOption {
type = str;
default = if hasSuffix ".exe" config.relativePath || hasSuffix ".dll" config.relativePath then "775" else "664";
};
dir = mkOption {
type = str;
default = "2775";
};
};
ownerFor = mkOption {
type = functionTo str;
};
srcPathFor = mkOption {
type = functionTo path;
};
srcStyle = mkOption {
type = enum [ "empty" "copy" "symlink" "symlink-shallow" ];
default = "symlink";
};
workingPathFor = mkOption {
type = functionTo path;
};
init = mkOption {
type = nullOr path;
};
initFor = mkOption {
type = functionTo (nullOr path);
};
initStyle = mkOption {
type = enum [ "none" "copy" "symlink" "symlink-shallow" ];
default = "copy";
};
setup = {
shared = mkOption {
type = functionTo lines;
internal = true;
};
script = mkOption {
type = functionTo lines;
internal = true;
};
};
};
config = let
versionPathFor = version: optionalString config.versioned "/${version}";
in {
init = mkOptionDefault (
if config.target == "game" then null
else if config.type == "directory" then "${emptyDir}"
else if hasSuffix ".json" config.relativePath then "${emptyJson}"
else if hasSuffix ".dll" config.relativePath || hasSuffix ".exe" config.relativePath then "${emptyExecutable}"
else "${emptyFile}"
);
initFor = mkOptionDefault (
{ user, version }: config.init
);
ownerFor = mkOptionDefault (user:
if config.target == "user" then user else "admin"
);
srcPathFor = mkOptionDefault ({ user, version }:
{
shared = cfg.sharedDataDir + versionPathFor version;
user = cfg.dataDirFor user + versionPathFor version;
game = cfg.gameDirFor version;
}.${config.target} or (throw "unsupported target")
+ "/${config.relativePath}"
);
workingPathFor = mkOptionDefault ({ user, version }:
cfg.workingDirFor { inherit user version; }
+ "/${config.relativePath}"
);
# TODO: setup.shared and do inits seperately!
setup.script = { user, version }@args: let
owner = config.ownerFor user;
srcPath = config.srcPathFor args;
workingPath = config.workingPathFor args;
initPath = config.initFor args;
parentWorkingPath = dirOf workingPath;
mkdir = dest: ''
if [[ -L ${escapeShellArg dest} ]]; then
rm -f ${escapeShellArg dest}
fi
if [[ ! -d ${escapeShellArg dest} ]]; then
mkdir -m${config.mode.dir} ${escapeShellArg dest}
else
chmod ${config.mode.dir} ${escapeShellArg dest}
fi
chown ${owner}:${cfg.group} ${escapeShellArg dest}
'';
mkStyle = { style, src }: if style != "none" && src == {
file = "${emptyFile}";
directory = "${emptyDir}";
}.${config.type} then "empty" else style;
doInit = { style, src, dest }: {
none = "true";
copy = {
file = ''
if [[ -L ${escapeShellArg dest} ]]; then
rm -f ${escapeShellArg dest}
elif [[ -e ${escapeShellArg dest} ]]; then
echo ERR: something is in the way of copying ${escapeShellArg dest} >&2
exit 1
fi
cp -TP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest}
chmod ${config.mode.file} ${escapeShellArg dest}
chown ${owner}:${cfg.group} ${escapeShellArg dest}
'';
directory = ''
${mkdir dest}
cp -rTP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest}
chown -R ${owner}:${cfg.group} ${escapeShellArg dest}
find ${escapeShellArg dest} -type f -exec chmod -m${config.mode.file} "{}" \;
'';
}.${config.type};
empty = {
directory = ''
${mkdir dest}
'';
file = ''
touch ${escapeShellArg dest}
chmod ${config.mode.file} ${escapeShellArg dest}
chown ${owner}:${cfg.group} ${escapeShellArg dest}
'';
}.${config.type};
symlink = ''
if [[ -e ${escapeShellArg dest} && ! -L ${escapeShellArg dest} ]]; then
echo ERR: something is in the way of linking ${escapeShellArg dest} >&2
exit 1
fi
ln -sfT ${escapeShellArg src} ${escapeShellArg dest}
'';
symlink-shallow = {
directory = ''
${mkdir dest}
ln -sf ${escapeShellArg src}/* ${escapeShellArg dest}/
'';
}.${config.type};
}.${mkStyle { inherit style src; }};
doSetup = { style, src, dest }: rec {
none = "true";
copy = {
file = ''
${empty}
'';
directory = ''
${empty}
if [[ ${escapeShellArg dest}/* != ${escapeShellArg dest}/\* ]]; then
chmod -m${config.mode.file} ${escapeShellArg dest}/*
fi
'';
}.${config.type};
empty = {
directory = ''
chmod ${config.mode.dir} ${escapeShellArg dest}
chown ${owner}:${cfg.group} ${escapeShellArg dest}
'';
file = ''
chmod ${config.mode.file} ${escapeShellArg dest}
chown ${owner}:${cfg.group} ${escapeShellArg dest}
'';
}.${config.type};
symlink = "true";
symlink-shallow = {
directory = ''
${mkdir.directory}
'';
}.${config.type};
}.${mkStyle { inherit style src; }};
init = doInit {
style = config.initStyle;
src = initPath;
dest = srcPath;
};
setup = doSetup {
style = config.initStyle;
src = initPath;
dest = srcPath;
};
src = doInit {
style = config.srcStyle;
src = srcPath;
dest = workingPath;
};
checkFlag = {
file = {
none = "e";
copy = "f";
symlink = "L";
}.${config.initStyle};
directory = {
none = "e";
copy = "d";
symlink-shallow = "d";
symlink = "L";
}.${config.initStyle};
}.${config.type};
checkParent = ''
if [[ ! -d ${escapeShellArg parentWorkingPath} ]]; then
echo ERR: parent of ${escapeShellArg workingPath} does not exist >&2
exit 1
fi
'';
check = if initPath != null then ''
if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then
${init}
else
${setup}
fi
'' else ''
if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then
echo ERR: src ${escapeShellArg srcPath} for ${escapeShellArg workingPath} does not exist >&2
exit 1
fi
'';
in ''
${checkParent}
${check}
${src}
'';
};
};
emptyFile = pkgs.writeText "empty.txt" "";
emptyJson = pkgs.writeText "empty.json" "{}";
emptyDir = pkgs.runCommand "empty" { } ''
mkdir $out
'';
emptyExecutable = pkgs.writeTextFile {
name = "empty.exe";
executable = true;
text = "";
};
bsdata = "Beat Saber_Data";
vars = let
bsUserData = mkSharePath (cfg.dataDirFor "%GENSO_STEAM_USER%");
bsWorkingData = mkSharePath (cfg.workingDirFor {
user = "%GENSO_STEAM_USER%";
version = "%GENSO_STEAM_BS_VERSION%";
});
in ''
if "%GENSO_STEAM_MACHINE%" == "" set "GENSO_STEAM_MACHINE=%COMPUTERNAME%"
if "%GENSO_STEAM_LOCAL_DATA%" == "" set "GENSO_STEAM_LOCAL_DATA=C:\Program Files\GensokyoZone"
if "%GENSO_STEAM_LOCAL_DATA_BS%" == "" set "GENSO_STEAM_LOCAL_DATA_BS=%GENSO_STEAM_LOCAL_DATA%\${cfg.dirName}"
if "%GENSO_STEAM_INSTALL%" == "" set "GENSO_STEAM_INSTALL=C:\Program Files (x86)\Steam"
if "%GENSO_STEAM_LIBRARY_BS%" == "" set "GENSO_STEAM_LIBRARY_BS=%GENSO_STEAM_INSTALL%"
if "%GENSO_STEAM_BS_VERSION%" == "" set "GENSO_STEAM_BS_VERSION=${cfg.defaultVersion}"
if "%GENSO_SMB_HOST%" == "" set "GENSO_SMB_HOST=smb.${config.networking.domain}"
if "%GENSO_SMB_SHARED_MOUNT%" == "" set "GENSO_SMB_SHARED_MOUNT=\\%GENSO_SMB_HOST%\shared"
set "STEAM_BS_LIBRARY=%GENSO_STEAM_LIBRARY_BS%\steamapps\common\Beat Saber"
set "STEAM_BS_APPDATA=%USERPROFILE%\AppData\LocalLow\Hyperbolic Magnetism\Beat Saber"
set "STEAM_BS_LOCAL_DATA=%GENSO_STEAM_LOCAL_DATA_BS%\%GENSO_STEAM_BS_VERSION%"
set "STEAM_BS_USER_DATA=${bsUserData}"
set "STEAM_BS_WORKING_DATA=${bsWorkingData}"
if "%GENSO_STEAM_BS_LOCAL%" == "1" (
set "STEAM_BS_LAUNCH=%STEAM_BS_LOCAL_DATA%"
set "STEAM_BS_LAUNCH_APPDATA=%STEAM_BS_LOCAL_DATA%\AppData"
) else (
set "STEAM_BS_LAUNCH=%STEAM_BS_WORKING_DATA%"
set "STEAM_BS_LAUNCH_APPDATA=%STEAM_BS_USER_DATA%\AppData"
)
if "%GENSO_STEAM_USER%" == "" goto NOUSER
'';
eof = ''
goto:eof
:NOUSER
echo no steam user set
'';
mount = ''
rmdir "%STEAM_BS_APPDATA%"
mklink /D "%STEAM_BS_APPDATA%" "%STEAM_BS_LAUNCH_APPDATA%"
rmdir "%STEAM_BS_LIBRARY%"
mklink /D "%STEAM_BS_LIBRARY%" "%STEAM_BS_LAUNCH%"
'';
setup = ''
rmdir "%STEAM_BS_APPDATA%"
rmdir "%STEAM_BS_LIBRARY%"
mkdir "%GENSO_STEAM_LOCAL_DATA_BS%"
move /Y "%STEAM_BS_LIBRARY%" "%GENSO_STEAM_LOCAL_DATA_BS%\Vanilla"
move /Y "%STEAM_BS_APPDATA%" "%GENSO_STEAM_LOCAL_DATA_BS%\Vanilla\AppData"
'';
mountbeatsaber = ''
${vars}
${mount}
${eof}
'';
launchbeatsaber = ''
${vars}
${mount}
cd /d "%STEAM_BS_LIBRARY%"
"%STEAM_BS_LIBRARY%\Beat Saber.exe"
${eof}
'';
fpfcbeatsaber = ''
${vars}
cd /d "%STEAM_BS_LIBRARY%"
"%STEAM_BS_LIBRARY%\Beat Saber.exe" fpfc
${eof}
'';
setupbeatsaber = ''
${vars}
${setup}
pause
${eof}
'';
localbeatsaber-mount = ''
set GENSO_STEAM_BS_LOCAL=1
${vars}
${mount}
if NOT "%GENSO_STEAM_BS_VERSION%" == "Vanilla" (
rmdir "%STEAM_BS_LIBRARY%\UserData"
mklink /D "%STEAM_BS_LIBRARY%\UserData" "%STEAM_BS_WORKING_DATA%\UserData"
)
${eof}
'';
localbeatsaber-vanilla = ''
set GENSO_STEAM_BS_VERSION=Vanilla
set GENSO_STEAM_BS_LOCAL=1
${vars}
${mount}
${eof}
'';
localbeatsaber-launch = ''
set GENSO_STEAM_BS_LOCAL=1
${vars}
${mount}
cd /d "%STEAM_BS_LIBRARY%"
"%STEAM_BS_LIBRARY%\Beat Saber.exe"
${eof}
'';
vanilla = ''
setx GENSO_STEAM_BS_VERSION Vanilla
'';
mksetupbeatsaber = { user, version }: let
setupFiles = mapAttrsToList (_: file: file.setup.script { inherit user version; }) cfg.files;
in pkgs.writeShellScript "setupbeatsaber-${user}-${version}" ''
set -eu
export PATH="$PATH:${makeBinPath [ pkgs.coreutils ]}"
${concatStringsSep "\n" setupFiles}
'';
in {
options.services.steam.beatsaber = with lib.types; {
enable = mkEnableOption "beatsaber scripts";
setup = mkEnableOption "beatsaber data" // {
default = accountSwitch.setup;
};
group = mkOption {
type = str;
default = "beatsaber";
};
defaultVersion = mkOption {
type = str;
};
versions = mkOption {
type = attrsOf (submodule versionModule);
default = { };
};
setupServiceNames = mkOption {
type = listOf str;
readOnly = true;
};
files = mkOption {
type = attrsOf (submodule fileModule);
default = { };
};
users = mkOption {
type = listOf str;
};
dirName = mkOption {
type = str;
default = "BeatSaber";
};
binDir = mkOption {
type = path;
default = accountSwitch.binDir + "/beatsaber";
};
gamesDir = mkOption {
type = path;
default = accountSwitch.gamesDir + "/${cfg.dirName}";
};
gameDirFor = mkOption {
type = functionTo path;
default = version: cfg.gamesDir + "/${version}";
};
sharedDataDir = mkOption {
type = path;
default = accountSwitch.sharedDataDir + "/${cfg.dirName}";
};
userDataDir = mkOption {
type = path;
default = accountSwitch.dataDir;
};
dataDirFor = mkOption {
type = functionTo path;
default = user: cfg.userDataDir + "/${user}/${cfg.dirName}";
};
userWorkingDir = mkOption {
type = path;
default = accountSwitch.workingDir;
};
userWorkingDirFor = mkOption {
type = functionTo path;
default = user: cfg.userWorkingDir + "/${user}/${cfg.dirName}";
};
workingDirFor = mkOption {
type = functionTo path;
default = { user, version }: cfg.userWorkingDirFor user + "/${version}";
};
};
config = {
services.steam.beatsaber = let
bsUsers = filterAttrs (_: userIs cfg.group) config.users.users;
allVersions = mapAttrsToList (_: version: version.version) cfg.versions;
gameFiles = {
"Beat Saber.exe" = { };
"UnityCrashHandler64.exe" = { };
"UnityPlayer.dll" = { };
"MonoBleedingEdge".type = "directory";
};
sharedFiles = {
DynamicOpenVR = {
type = "directory";
versioned = true;
};
IPA = {
type = "directory";
versioned = true;
};
Libs = {
type = "directory";
versioned = true;
};
Plugins = {
type = "directory";
versioned = true;
};
Logs = {
type = "directory";
versioned = true;
};
"BeatSaberVersion.txt" = {
versioned = true;
initFor = { version, ... }: pkgs.writeText "BeatSaberVersion-${version}.txt" version;
};
"IPA.exe".versioned = true;
"IPA.exe.config".versioned = true;
"IPA.runtimeconfig.json".versioned = true;
"winhttp.dll".versioned = true;
${bsdata} = {
type = "directory";
versioned = true;
#initStyle = "symlink-shallow";
#initFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}";
initStyle = "none";
srcPathFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}";
srcStyle = "symlink-shallow";
};
"${bsdata}/Managed" = {
type = "directory";
versioned = true;
initFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}/Managed";
};
# TODO: remove this to use multiple folders
"${bsdata}/CustomLevels" = {
type = "directory";
initStyle = "none";
srcPathFor = { ... }: cfg.sharedDataDir + "/CustomLevels";
};
CustomAvatars = {
type = "directory";
initStyle = "none";
};
CustomNotes = {
type = "directory";
initStyle = "none";
};
CustomPlatforms = {
type = "directory";
initStyle = "none";
};
CustomSabers = {
type = "directory";
initStyle = "none";
};
CustomWalls = {
type = "directory";
initStyle = "none";
};
Playlists = {
type = "directory";
initStyle = "none";
};
"UserData/ScoreSaber/Replays" = {
type = "directory";
initStyle = "none";
srcPathFor = { ... }: cfg.sharedDataDir + "/Replays";
};
"UserData/Beat Saber IPA.json".versioned = true;
"UserData/SongCore/" = {
versioned = true;
relativePath = "UserData/SongCore";
type = "directory";
srcStyle = "empty";
};
"UserData/SongCore/folders.xml" = {
versioned = true;
};
"UserData/SongCore/SongCoreExtraData.dat" = {
versioned = true;
init = "${emptyJson}";
};
"UserData/SongCore/SongDurationCache.dat" = {
versioned = true;
init = "${emptyJson}";
};
"UserData/SongCore/SongHashData.dat" = {
versioned = true;
init = "${emptyJson}";
};
"UserData/Chroma".type = "directory";
"UserData/Nya".type = "directory";
"UserData/SongRankedBadge".type = "directory";
"UserData/DrinkWater".type = "directory";
"UserData/Enhancements".type = "directory";
"UserData/HitScoreVisualizer" = {
type = "directory";
# TODO: initStyle = "symlink"; init = "${myhitscorejsons}";
};
"UserData/Saber Factory/" = {
relativePath = "UserData/Saber Factory";
type = "directory";
srcStyle = "empty";
};
"UserData/Saber Factory/Cache".type = "directory";
"UserData/Saber Factory/Textures".type = "directory";
"UserData/BeatSaverDownloader.ini" = { };
"UserData/BeatSaverUpdater.json" = { };
"UserData/SongDetailsCache.proto".versioned = true;
"UserData/SongDetailsCache.proto.Direct.etag".versioned = true;
};
userFiles = {
"UserData" = {
type = "directory";
versioned = true;
srcStyle = "empty";
};
"UserData/Camera2".type = "directory";
"UserData/Saber Factory" = {
type = "directory";
srcStyle = "empty";
};
"UserData/Saber Factory/Presets".type = "directory";
"UserData/Saber Factory/TrailConfig.json" = { };
"UserData/SongCore" = {
type = "directory";
versioned = true;
srcStyle = "empty";
};
"UserData/SongCore/SongCore.json" = {
versioned = true;
};
"UserData/ScoreSaber" = {
type = "directory";
versioned = true;
srcStyle = "empty";
};
"UserData/ScoreSaber/ScoreSaber.json" = {
versioned = true;
};
"UserData/Disabled Mods.json".versioned = true;
"UserData/modprefs.ini".versioned = true;
"UserData/JDFixer.json".versioned = true;
};
userDataFiles = [
"modprefs.ini" "Disabled Mods.json"
"AutoPauseStealth.json"
"BeatSaberMarkupLanguage.json"
"BeatSaviorData.ini"
"BetterSongList.json"
"BetterSongSearch.json"
"bookmarkedSongs.json" "votedSongs.json"
"Chroma.json"
"Cinema.json"
"CountersPlus.json"
"CustomAvatars.CalibrationData.dat" "CustomAvatars.json" "CustomNotes.json" "Custom Platforms.json" "CustomWalls.json"
"DrinkWater.json"
"EasyOffset.json"
"Enhancements.json"
"FasterScroll.json"
"FastFail.json"
"Fifth Anniversary Mod.json"
"Gotta Go Fast.json"
"Heck.json"
"HitScoreVisualizer.json"
"HitsoundTweaks.json"
"Intro Skip.json"
"JDFixer.json"
"ModelDownloader.json"
"Music Spatializer.json"
"Nya.json"
"ParticleOverdrive.ini"
"PerformanceMeter.json"
"PlayFirst.json"
"PlaylistManager.json"
"PracticePlugin.json"
"Saber Factory.json"
"SaberTailor.json"
"ScorePercentage.json"
"SiraUtil.json"
"SmoothCamPlus.json"
"SongChartVisualizer.json"
"SongPlayData.json"
"SongPlayHistory.json"
"SongRankedBadge.json"
"Technicolor.json"
"Tweaks55.json"
"UITweaks.json"
];
mapSharedFile = file: file // {
target = "shared";
};
mapGameFile = file: file // {
target = "game";
};
mapUserDataFile = file: nameValuePair "UserData/${file}" {
target = "user";
};
in {
defaultVersion = mkIf (allVersions != [ ]) (mkOptionDefault (
head allVersions
));
users = mkOptionDefault (
mapAttrsToList (_: user: user.name) bsUsers
);
setupServiceNames = mkOptionDefault (
map (user: "steam-setup-beatsaber-${user}.service") cfg.users
);
files = mkMerge [
userFiles
(listToAttrs (map mapUserDataFile userDataFiles))
(mapAttrs (_: mapGameFile) gameFiles)
(mapAttrs (_: mapSharedFile) sharedFiles)
];
};
systemd.services.steam-setup-beatsaber = mkIf cfg.setup {
wantedBy = [
"multi-user.target"
];
after = [
"tmpfiles.service"
];
serviceConfig = {
Type = mkOptionDefault "oneshot";
RemainAfterExit = mkOptionDefault true;
ExecStart = mkMerge (map (user:
(mapAttrsToList (_: version:
"${mksetupbeatsaber { inherit user; inherit (version) version; }}"
) cfg.versions)
) cfg.users);
};
};
services.tmpfiles = let
toplevel = {
owner = mkDefault "admin";
group = mkDefault cfg.group;
mode = mkDefault "3775";
};
shared = {
inherit (toplevel) owner group;
mode = mkDefault "2775";
};
personal = owner: {
inherit owner;
inherit (shared) group mode;
};
bin = {
inherit (toplevel) owner group;
mode = "0755";
type = "copy";
};
sharedFolders = [
"CustomAvatars"
"CustomLevels"
"CustomNotes"
"CustomPlatforms"
"CustomSabers"
"CustomWalls"
"Playlists"
"Replays"
"AppData"
"UserData"
];
setupFiles = [
{
${cfg.sharedDataDir} = toplevel;
${cfg.binDir} = shared;
}
(listToAttrs (
map (folder:
nameValuePair "${cfg.sharedDataDir}/${folder}" shared
) sharedFolders
))
] ++ concatMap (owner:
singleton {
${cfg.dataDirFor owner} = personal owner;
"${cfg.dataDirFor owner}/AppData" = personal owner;
"${cfg.dataDirFor owner}/UserData" = personal owner;
} ++ mapAttrsToList (_: version: {
"${cfg.dataDirFor owner}/${version.version}" = personal owner;
${cfg.userWorkingDirFor owner} = personal owner;
${cfg.workingDirFor { user = owner; inherit (version) version; }} = personal owner;
}) cfg.versions
) cfg.users
++ mapAttrsToList (_: version: {
"${cfg.sharedDataDir}/${version.version}" = shared;
}) cfg.versions;
versionBinFiles = mapAttrs' (_: version: nameValuePair
"${cfg.binDir}/${replaceStrings [ "." ] [ "_" ] version.version}.bat"
{
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-${version.version}.bat";
executable = true;
text = ''
setx GENSO_STEAM_BS_VERSION ${version.version}
'';
};
}
) cfg.versions;
binFiles = {
"${cfg.binDir}/mount.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-mount.bat";
executable = true;
text = mountbeatsaber;
};
};
"${cfg.binDir}/launch.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-launch.bat";
executable = true;
text = launchbeatsaber;
};
};
"${cfg.binDir}/fpfc.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-fpfc.bat";
executable = true;
text = fpfcbeatsaber;
};
};
"${cfg.binDir}/setup.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-setup.bat";
executable = true;
text = setupbeatsaber;
};
};
"${cfg.binDir}/local-launch.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-launch.bat";
executable = true;
text = localbeatsaber-launch;
};
};
"${cfg.binDir}/local-mount.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-mount.bat";
executable = true;
text = localbeatsaber-mount;
};
};
"${cfg.binDir}/local-vanilla.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-local-vanilla.bat";
executable = true;
text = localbeatsaber-vanilla;
};
};
"${cfg.binDir}/vanilla.bat" = {
inherit (bin) owner group mode type;
src = pkgs.writeTextFile {
name = "beatsaber-version-vanilla.bat";
executable = true;
text = vanilla;
};
};
"${cfg.binDir}/ModAssistant.exe" = {
inherit (bin) owner group mode type;
src = pkgs.fetchurl {
url = "https://github.com/Assistant/ModAssistant/releases/download/v1.1.32/ModAssistant.exe";
hash = "sha256-ozu2gYFiz+2BjptqL80DmUopbahbyGKFO1IPd7BhVPM=";
executable = true;
};
};
} // versionBinFiles;
in {
enable = mkIf cfg.setup true;
files = mkIf cfg.setup (mkMerge (
singleton binFiles
++ setupFiles
));
};
};
}