Files
UltraWidePatches/install.sh
T
ckochandClaude Opus 4.8 5d6fd277c9 Restructure into multi-game UltraWidePatches umbrella
Generalize the project from a single GB4 fix into a collection that can host
ultrawide patches for many games.

- games/<Game>/{SUWSF.ini,game.conf,README.md}; GB4 moved in as the first entry
- game.conf carries all game-specific data (appid, exe, paths, loader, launch
  option), so install.sh is fully generic: ./install.sh <Game> [uninstall]
- installer gains GAME_DIR / PREFIX_INI_FILE / LOADER_NAME overrides; verified
  install -> idempotent re-run -> uninstall leaves the tree pristine
- tools de-hardcoded: dump_decrypted.py takes an exe name, read_protonlog.sh
  takes an appid, aob.py takes a dump + game ini, build_release.sh takes a game
- README rewritten as an umbrella index with an "adding a game" guide
- LICENSE copyright -> programmingPug

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 11:21:20 -04:00

147 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# ============================================================================
# UltraWidePatches - installer (Linux / Proton, also usable on Windows via
# manual extraction of the release zip)
#
# Usage:
# ./install.sh # list available games
# ./install.sh GundamBreaker4 # install that game's patch
# ./install.sh GundamBreaker4 uninstall
#
# Overrides:
# GAME_DIR=/path/to/Binaries/Win64 ./install.sh <Game> # nonstandard install
# PREFIX_INI_FILE=/path/to/Engine.ini ./install.sh <Game> # nonstandard prefix
# LOADER_NAME=version.dll ./install.sh <Game> # loader collision
# ============================================================================
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GAMES_DIR="$HERE/games"
list_games() {
echo "Available patches:"
for d in "$GAMES_DIR"/*/; do
[ -f "$d/game.conf" ] || continue
( TITLE=""; STATUS=""; . "$d/game.conf" >/dev/null 2>&1 || true
printf " %-22s %s\n" "$(basename "$d")" "${TITLE:-}${STATUS:-}" )
done
echo
echo "Usage: ./install.sh <GameName> [uninstall]"
}
GAME="${1:-}"
ACTION="${2:-install}"
if [ -z "$GAME" ]; then list_games; exit 0; fi
GAME_CONF="$GAMES_DIR/$GAME/game.conf"
if [ ! -f "$GAME_CONF" ]; then
echo "[!] Unknown game: $GAME"; echo; list_games; exit 1
fi
# shellcheck disable=SC1090
. "$GAME_CONF"
LOADER="${LOADER_NAME:-${LOADER:-dsound.dll}}"
echo "=== ${TITLE:-$GAME} ==="
# --- locate the game's exe directory across Steam libraries -------------------
find_gamedir() {
local cand
for base in \
"$HOME/.steam/steam" "$HOME/.local/share/Steam" \
"/media/$USER"/*/steam "/media/$USER"/*/SteamLibrary \
"$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam"; do
cand="$base/steamapps/common/$COMMON_SUBDIR"
[ -f "$cand/$EXE" ] && { echo "$cand"; return 0; }
done
cand=$(find /home /media /mnt -maxdepth 8 -name "$EXE" 2>/dev/null | head -1)
[ -n "$cand" ] && { dirname "$cand"; return 0; }
return 1
}
GAMEDIR="${GAME_DIR:-$(find_gamedir || true)}"
if [ -z "${GAMEDIR:-}" ] || [ ! -f "$GAMEDIR/$EXE" ]; then
echo "[!] Could not find $EXE."
echo " Set it manually: GAME_DIR=/path/to/exe/dir ./install.sh $GAME"
exit 1
fi
echo "[+] Game dir: $GAMEDIR"
# --- locate the Proton prefix ini (optional, per-game) ------------------------
find_prefix_ini() {
[ -n "${PREFIX_INI_FILE:-}" ] && { [ -f "$PREFIX_INI_FILE" ] && echo "$PREFIX_INI_FILE" && return 0; return 1; }
[ -z "${PREFIX_INI:-}" ] && return 1
local cand
for base in \
"$HOME/.steam/steam" "$HOME/.local/share/Steam" \
"/media/$USER"/*/steam "/media/$USER"/*/SteamLibrary \
"$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam"; do
cand="$base/steamapps/compatdata/$APPID/pfx/$PREFIX_INI"
[ -f "$cand" ] && { echo "$cand"; return 0; }
done
return 1
}
MARK_BEGIN="; >>> UltraWidePatches ($GAME) >>>"
MARK_END="; <<< UltraWidePatches ($GAME) <<<"
apply_ini() {
[ -z "${INI_BLOCK:-}" ] && return
local ini; ini="$(find_prefix_ini || true)"
[ -z "$ini" ] && { echo "[i] Prefix ini not found (launch the game once first); skipping config tweak."; return; }
if grep -qF "$MARK_BEGIN" "$ini"; then echo "[i] Config tweak already present."; return; fi
cp "$ini" "$ini.bak-uwp"
{ echo ""; echo "$MARK_BEGIN"; echo "$INI_BLOCK"; echo "$MARK_END"; } >> "$ini"
echo "[+] Config tweak applied (backup: $(basename "$ini").bak-uwp)"
}
remove_ini() {
local ini; ini="$(find_prefix_ini || true)"
[ -z "$ini" ] && return
if grep -qF "$MARK_BEGIN" "$ini"; then
local esc_b esc_e
esc_b=$(printf '%s' "$MARK_BEGIN" | sed 's/[][\/.*^$]/\\&/g')
esc_e=$(printf '%s' "$MARK_END" | sed 's/[][\/.*^$]/\\&/g')
sed -i "/$esc_b/,/$esc_e/d" "$ini"
echo "[+] Config tweak removed."
fi
}
if [ "$ACTION" = "uninstall" ]; then
echo "[+] Uninstalling..."
for f in SUWSF.asi SUWSF.ini dsound.dll dinput8.dll version.dll winmm.dll; do
[ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f"
done
remove_ini
echo "[+] Done. No game file was ever modified."
exit 0
fi
# --- install ------------------------------------------------------------------
ASI="$HERE/vendor/SUWSF-x64/SUWSF.asi"
LOADER_SRC="$HERE/vendor/SUWSF-x64/dsound.dll" # Ultimate ASI Loader (adapts by filename)
INI="$GAMES_DIR/$GAME/SUWSF.ini"
for f in "$ASI" "$LOADER_SRC" "$INI"; do
[ -f "$f" ] || { echo "[!] missing $f"; exit 1; }
done
cp -v "$ASI" "$GAMEDIR/SUWSF.asi"
cp -v "$INI" "$GAMEDIR/SUWSF.ini"
cp -v "$LOADER_SRC" "$GAMEDIR/$LOADER"
apply_ini
cat <<EOF
[+] Installed: SUWSF.asi, SUWSF.ini, $LOADER
NEXT (Proton) — REQUIRED, or the loader is ignored and the fix won't apply in gameplay.
Steam > right-click ${TITLE:-$GAME} > Properties > Launch Options:
${LAUNCH_OPTION:-WINEDLLOVERRIDES="${LOADER%.dll}=n,b" %command%}
Then set the in-game resolution to your native ultrawide res, fullscreen.
Troubleshooting:
Different loader name: LOADER_NAME=version.dll ./install.sh $GAME
Diagnose injection: add PROTON_LOG=1 to launch options, then
./tools/read_protonlog.sh $APPID
Remove everything: ./install.sh $GAME uninstall
EOF