From ca5db2231b8554d1377dd449f6fb9c736e3d6386 Mon Sep 17 00:00:00 2001 From: xXJsonDeruloXx Date: Fri, 20 Mar 2026 17:32:05 -0400 Subject: Implement prefix-managed OptiScaler runtime --- defaults/assets/fgmod.sh | 335 ++++++++++++++++++++--------------------------- 1 file changed, 145 insertions(+), 190 deletions(-) (limited to 'defaults/assets/fgmod.sh') diff --git a/defaults/assets/fgmod.sh b/defaults/assets/fgmod.sh index fa36558..7aa62bb 100755 --- a/defaults/assets/fgmod.sh +++ b/defaults/assets/fgmod.sh @@ -1,227 +1,182 @@ #!/usr/bin/env bash +set -euo pipefail set -x -exec > >(tee -i /tmp/fgmod-install.log) 2>&1 +exec > >(tee -i /tmp/fgmod-prefix-managed.log) 2>&1 + +log() { + echo "$*" + logger -t fgmod-prefix-managed "$*" +} error_exit() { - echo "โŒ $1" - if [[ -n $STEAM_ZENITY ]]; then - $STEAM_ZENITY --error --text "$1" - else - zenity --error --text "$1" || echo "Zenity failed to display error" + local message="$1" + echo "โŒ $message" + logger -t fgmod-prefix-managed "ERROR: $message" + if [[ -n "${STEAM_ZENITY:-}" ]]; then + "$STEAM_ZENITY" --error --text "$message" || true + elif command -v zenity >/dev/null 2>&1; then + zenity --error --text "$message" || true fi - logger -t fgmod "โŒ ERROR: $1" exit 1 } -# === CONFIG === -fgmod_path="$HOME/fgmod" -dll_name="${DLL:-dxgi.dll}" -preserve_ini="${PRESERVE_INI:-true}" - -# === Resolve Game Path === -if [[ "$#" -lt 1 ]]; then - error_exit "Usage: $0 program [program_arguments...]" -fi +bundle_root="${HOME}/fgmod" +managed_dir_name="optiscaler-managed" +manifest_name="manifest.env" +proxy_name="${OPTISCALER_PROXY:-${DLL:-winmm}}" +proxy_name="${proxy_name%.dll}" +proxy_dll="${proxy_name}.dll" +backup_dll="${proxy_name}-original.dll" + +support_files=( + "libxess.dll" + "libxess_dx11.dll" + "libxess_fg.dll" + "libxell.dll" + "amd_fidelityfx_dx12.dll" + "amd_fidelityfx_framegeneration_dx12.dll" + "amd_fidelityfx_upscaler_dx12.dll" + "amd_fidelityfx_vk.dll" + "nvngx.dll" + "dlssg_to_fsr3_amd_is_better.dll" + "fakenvapi.dll" + "fakenvapi.ini" +) + +case "$proxy_name" in + winmm|dxgi|version|dbghelp|winhttp|wininet|d3d12) ;; + *) error_exit "Unsupported OPTISCALER_PROXY '$proxy_name'." ;; +esac + +[[ -d "$bundle_root" ]] || error_exit "OptiScaler runtime not installed at $bundle_root" +[[ -n "${STEAM_COMPAT_DATA_PATH:-}" ]] || error_exit "STEAM_COMPAT_DATA_PATH is required. Use this wrapper from a Steam/Proton launch option." +[[ $# -ge 1 ]] || error_exit "Usage: $0 program [program_arguments...]" + +compatdata_path="$STEAM_COMPAT_DATA_PATH" +system32_path="$compatdata_path/pfx/drive_c/windows/system32" +managed_root="$compatdata_path/$managed_dir_name" +manifest_path="$managed_root/$manifest_name" +managed_ini="$managed_root/OptiScaler.ini" +managed_plugins="$managed_root/plugins" + +cleanup_proxy_stage() { + local cleanup_proxy="$1" + local cleanup_proxy_dll="${cleanup_proxy}.dll" + local cleanup_backup_dll="${cleanup_proxy}-original.dll" + + rm -f "$system32_path/$cleanup_proxy_dll" + if [[ -f "$system32_path/$cleanup_backup_dll" ]]; then + mv -f "$system32_path/$cleanup_backup_dll" "$system32_path/$cleanup_proxy_dll" + fi +} -exe_folder_path="" -if [[ $# -eq 1 ]]; then - [[ "$1" == *.exe ]] && exe_folder_path=$(dirname "$1") || exe_folder_path="$1" -else - for arg in "$@"; do - if [[ "$arg" == *.exe ]]; then - [[ "$arg" == *"Cyberpunk 2077"* ]] && arg=${arg//REDprelauncher.exe/bin/x64/Cyberpunk2077.exe} - [[ "$arg" == *"Witcher 3"* ]] && arg=${arg//REDprelauncher.exe/bin/x64_dx12/witcher3.exe} - [[ "$arg" == *"Baldurs Gate 3"* ]] && arg=${arg//Launcher\/LariLauncher.exe/bin/bg3_dx11.exe} - [[ "$arg" == *"HITMAN 3"* ]] && arg=${arg//Launcher.exe/Retail/HITMAN3.exe} - [[ "$arg" == *"HITMAN World of Assassination"* ]] && arg=${arg//Launcher.exe/Retail/HITMAN3.exe} - [[ "$arg" == *"SYNCED"* ]] && arg=${arg//Launcher\/sop_launcher.exe/SYNCED.exe} - [[ "$arg" == *"2KLauncher"* ]] && arg=${arg//2KLauncher\/LauncherPatcher.exe/DoesntMatter.exe} - [[ "$arg" == *"Warhammer 40,000 DARKTIDE"* ]] && arg=${arg//launcher\/Launcher.exe/binaries/Darktide.exe} - [[ "$arg" == *"Warhammer Vermintide 2"* ]] && arg=${arg//launcher\/Launcher.exe/binaries_dx12/vermintide2_dx12.exe} - [[ "$arg" == *"Satisfactory"* ]] && arg=${arg//FactoryGameSteam.exe/Engine/Binaries/Win64/FactoryGameSteam-Win64-Shipping.exe} - [[ "$arg" == *"FINAL FANTASY XIV Online"* ]] && arg=${arg//boot\/ffxivboot.exe/game/ffxiv_dx11.exe} - exe_folder_path=$(dirname "$arg") - break - fi +cleanup_stage_files() { + local cleanup_proxy="$1" + rm -f "$system32_path/OptiScaler.ini" + for file_name in "${support_files[@]}"; do + rm -f "$system32_path/$file_name" done -fi - -for arg in "$@"; do - if [[ "$arg" == lutris:rungameid/* ]]; then - lutris_id="${arg#lutris:rungameid/}" - - # Get slug from Lutris JSON - slug=$(lutris --list-games --json 2>/dev/null | jq -r ".[] | select(.id == $lutris_id) | .slug") - - if [[ -z "$slug" || "$slug" == "null" ]]; then - echo "Could not find slug for Lutris ID $lutris_id" - break - fi - - # Find matching YAML file using slug - config_file=$(find ~/.config/lutris/games/ -iname "${slug}-*.yml" | head -1) - - if [[ -z "$config_file" ]]; then - echo "No config file found for slug '$slug'" - break - fi + rm -f "$system32_path/OptiScaler.log" + rm -rf "$system32_path/plugins" + cleanup_proxy_stage "$cleanup_proxy" +} - # Extract executable path from YAML - exe_path=$(grep -E '^\s*exe:' "$config_file" | sed 's/.*exe:[[:space:]]*//' ) +mkdir -p "$system32_path" "$managed_root" "$managed_plugins" - if [[ -n "$exe_path" ]]; then - exe_folder_path=$(dirname "$exe_path") - echo "Resolved executable path: $exe_path" - echo "Executable folder: $exe_folder_path" - else - echo "Executable path not found in $config_file" - fi +existing_proxy="" +if [[ -f "$manifest_path" ]]; then + # shellcheck disable=SC1090 + source "$manifest_path" + existing_proxy="${MANAGED_PROXY:-}" +fi - break - fi -done +if [[ -n "$existing_proxy" && "$existing_proxy" != "$proxy_name" ]]; then + log "Switching managed proxy from $existing_proxy to $proxy_name" + cleanup_stage_files "$existing_proxy" +fi -[[ -z "$exe_folder_path" && -n "$STEAM_COMPAT_INSTALL_PATH" ]] && exe_folder_path="$STEAM_COMPAT_INSTALL_PATH" +[[ -f "$bundle_root/OptiScaler.ini" ]] || error_exit "Missing OptiScaler.ini in runtime bundle" +[[ -f "$bundle_root/update-optiscaler-config.py" ]] || error_exit "Missing update-optiscaler-config.py in runtime bundle" -if [[ -d "$exe_folder_path/Engine" ]]; then - ue_exe=$(find "$exe_folder_path" -maxdepth 4 -mindepth 4 -path "*Binaries/Win64/*.exe" -not -path "*/Engine/*" | head -1) - exe_folder_path=$(dirname "$ue_exe") +python_bin="python3" +if ! command -v "$python_bin" >/dev/null 2>&1; then + python_bin="python" fi +command -v "$python_bin" >/dev/null 2>&1 || error_exit "Python interpreter not found" -[[ ! -d "$exe_folder_path" ]] && error_exit "โŒ Could not resolve game directory!" -[[ ! -w "$exe_folder_path" ]] && error_exit "๐Ÿ›‘ No write permission to the game folder!" +if [[ ! -f "$managed_ini" ]]; then + cp -f "$bundle_root/OptiScaler.ini" "$managed_ini" +fi -logger -t fgmod "๐ŸŸข Target directory: $exe_folder_path" -logger -t fgmod "๐Ÿงฉ Using DLL name: $dll_name" -logger -t fgmod "๐Ÿ“„ Preserve INI: $preserve_ini" +"$python_bin" "$bundle_root/update-optiscaler-config.py" "$managed_ini" +sed -i 's/^UseHQFont[[:space:]]*=[[:space:]]*auto$/UseHQFont=false/' "$managed_ini" || true -# === Cleanup Old Injectors === -rm -f "$exe_folder_path"/{dxgi.dll,winmm.dll,nvngx.dll,_nvngx.dll,nvngx-wrapper.dll,dlss-enabler.dll,OptiScaler.dll} +if [[ -d "$bundle_root/plugins" ]]; then + rm -rf "$managed_plugins" + mkdir -p "$managed_plugins" + cp -f "$bundle_root/plugins"/* "$managed_plugins/" 2>/dev/null || true +fi -# === Optional: Backup Original DLLs === -original_dlls=("d3dcompiler_47.dll" "amd_fidelityfx_dx12.dll" "amd_fidelityfx_framegeneration_dx12.dll" "amd_fidelityfx_upscaler_dx12.dll" "amd_fidelityfx_vk.dll") -for dll in "${original_dlls[@]}"; do - [[ -f "$exe_folder_path/$dll" && ! -f "$exe_folder_path/$dll.b" ]] && mv -f "$exe_folder_path/$dll" "$exe_folder_path/$dll.b" +for file_name in "${support_files[@]}"; do + if [[ -f "$bundle_root/$file_name" ]]; then + cp -f "$bundle_root/$file_name" "$system32_path/$file_name" + fi done -# === Remove nvapi64.dll and its backup (conflicts from previous fakenvapi versions) === -rm -f "$exe_folder_path/nvapi64.dll" "$exe_folder_path/nvapi64.dll.b" -echo "๐Ÿงน Cleaned up nvapi64.dll and backup (legacy fakenvapi conflicts)" +if [[ -d "$managed_plugins" ]]; then + rm -rf "$system32_path/plugins" + mkdir -p "$system32_path/plugins" + cp -f "$managed_plugins"/* "$system32_path/plugins/" 2>/dev/null || true +fi -# === Core Install === -if [[ -f "$fgmod_path/renames/$dll_name" ]]; then - echo "โœ… Using pre-renamed $dll_name" - cp "$fgmod_path/renames/$dll_name" "$exe_folder_path/$dll_name" || error_exit "โŒ Failed to copy $dll_name" -else - echo "โš ๏ธ Pre-renamed $dll_name not found, falling back to OptiScaler.dll" - cp "$fgmod_path/OptiScaler.dll" "$exe_folder_path/$dll_name" || error_exit "โŒ Failed to copy OptiScaler.dll as $dll_name" +if [[ -f "$system32_path/$proxy_dll" && ! -f "$system32_path/$backup_dll" ]]; then + mv -f "$system32_path/$proxy_dll" "$system32_path/$backup_dll" fi -# === OptiScaler.ini Handling === -if [[ "$preserve_ini" == "true" && -f "$exe_folder_path/OptiScaler.ini" ]]; then - echo "๐Ÿ“„ Preserving existing OptiScaler.ini (user settings retained)" - logger -t fgmod "๐Ÿ“„ Existing OptiScaler.ini preserved in $exe_folder_path" +if [[ -f "$bundle_root/renames/$proxy_dll" ]]; then + cp -f "$bundle_root/renames/$proxy_dll" "$system32_path/$proxy_dll" else - echo "๐Ÿ“„ Installing OptiScaler.ini from plugin defaults" - cp "$fgmod_path/OptiScaler.ini" "$exe_folder_path/OptiScaler.ini" || error_exit "โŒ Failed to copy OptiScaler.ini" - logger -t fgmod "๐Ÿ“„ OptiScaler.ini installed to $exe_folder_path" + cp -f "$bundle_root/OptiScaler.dll" "$system32_path/$proxy_dll" fi -# === OptiScaler env variables Handling === -if [[ -f "$fgmod_path/update-optiscaler-config.py" ]]; then - python "$fgmod_path/update-optiscaler-config.py" "$exe_folder_path/OptiScaler.ini" +cp -f "$managed_ini" "$system32_path/OptiScaler.ini" + +runtime_version="unknown" +if [[ -f "$bundle_root/version.txt" ]]; then + runtime_version="$(<"$bundle_root/version.txt")" fi -# OptiScaler 0.9.0-pre11 can assert on Proton when HQ font auto mode tries to load -# an external TTF that is not present. Only normalize the default auto value. -sed -i 's/^UseHQFont[[:space:]]*=[[:space:]]*auto$/UseHQFont=false/' "$exe_folder_path/OptiScaler.ini" || true +cat > "$manifest_path" </dev/null 2>&1 - "$@" -else - echo "Done!" - echo "----------------------------------------" - echo "Debug Info (Standalone Mode):" - echo "Number of arguments: $#" - for i in $(seq 1 $#); do - echo "Arg $i: ${!i}" - done - echo "Final executable path: $exe_folder_path" - echo "----------------------------------------" - - # Also log standalone mode to journal - logger -t fgmod "==================" - logger -t fgmod "Debug Info (Standalone Mode):" - logger -t fgmod "Number of arguments: $#" - for i in $(seq 1 $#); do - logger -t fgmod "Arg $i: ${!i}" - done - logger -t fgmod "Final executable path: $exe_folder_path" - logger -t fgmod "==================" +log "Using compatdata path: $compatdata_path" +log "Using system32 path: $system32_path" +log "Using prefix-managed proxy: $proxy_dll" +log "Using WINEDLLOVERRIDES=$WINEDLLOVERRIDES" + +set +e +"$@" +exit_code=$? +set -e + +if [[ -f "$system32_path/OptiScaler.ini" ]]; then + cp -f "$system32_path/OptiScaler.ini" "$managed_ini" fi + +exit "$exit_code" -- cgit v1.2.3