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>
This commit is contained in:
+103
-65
@@ -1,108 +1,146 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# Gundam Breaker 4 - UltraWide Fix installer (Linux / Proton)
|
||||
# Installs SUWSF + Ultimate ASI Loader + our GB4 SUWSF.ini next to the
|
||||
# shipping exe. Idempotent. Use ./install.sh uninstall to remove.
|
||||
# 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)"
|
||||
LOADER_NAME="${LOADER_NAME:-dsound.dll}" # override: LOADER_NAME=version.dll ./install.sh
|
||||
GAMES_DIR="$HERE/games"
|
||||
|
||||
# --- locate GB4 shipping exe dir across Steam libraries -----------------------
|
||||
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/Data/steam" "/media/$USER"/*/steam \
|
||||
"/media/$USER"/*/steam "/media/$USER"/*/SteamLibrary \
|
||||
"$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam"; do
|
||||
cand="$base/steamapps/common/GBBBB/GB4/Binaries/Win64"
|
||||
[ -f "$cand/GB4-Win64-Shipping.exe" ] && { echo "$cand"; return 0; }
|
||||
cand="$base/steamapps/common/$COMMON_SUBDIR"
|
||||
[ -f "$cand/$EXE" ] && { echo "$cand"; return 0; }
|
||||
done
|
||||
# brute-force fallback
|
||||
cand=$(find /home /media /mnt -maxdepth 8 -name GB4-Win64-Shipping.exe 2>/dev/null | head -1)
|
||||
cand=$(find /home /media /mnt -maxdepth 8 -name "$EXE" 2>/dev/null | head -1)
|
||||
[ -n "$cand" ] && { dirname "$cand"; return 0; }
|
||||
return 1
|
||||
}
|
||||
|
||||
GAMEDIR="${GB4_WIN64_DIR:-$(find_gamedir || true)}"
|
||||
if [ -z "${GAMEDIR:-}" ] || [ ! -f "$GAMEDIR/GB4-Win64-Shipping.exe" ]; then
|
||||
echo "[!] Could not find GB4-Win64-Shipping.exe."
|
||||
echo " Set it manually: GB4_WIN64_DIR=/path/to/GB4/Binaries/Win64 ./install.sh"
|
||||
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"
|
||||
|
||||
ASI="$HERE/vendor/SUWSF-x64/SUWSF.asi"
|
||||
LOADER_SRC="$HERE/vendor/SUWSF-x64/dsound.dll" # Ultimate ASI Loader (adapts by filename)
|
||||
INI="$HERE/patch/SUWSF.ini"
|
||||
for f in "$ASI" "$LOADER_SRC" "$INI"; do
|
||||
[ -f "$f" ] || { echo "[!] missing $f"; exit 1; }
|
||||
done
|
||||
|
||||
# --- locate the Proton prefix Engine.ini (for the immediate menu/lobby win) ---
|
||||
find_engine_ini() {
|
||||
# --- 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 \
|
||||
"/media/$USER/Data/steam" "$HOME/.local/share/Steam" \
|
||||
"$HOME/.steam/steam" "/media/$USER"/*/steam; do
|
||||
cand="$base/steamapps/compatdata/1672500/pfx/drive_c/users/steamuser/AppData/Local/GB4/Saved/Config/WindowsNoEditor/Engine.ini"
|
||||
"$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="; >>> BG4 UltraWide Fix (MaintainYFOV) >>>"
|
||||
MARK_END="; <<< BG4 UltraWide Fix <<<"
|
||||
MARK_BEGIN="; >>> UltraWidePatches ($GAME) >>>"
|
||||
MARK_END="; <<< UltraWidePatches ($GAME) <<<"
|
||||
|
||||
apply_engine_ini() {
|
||||
local ini; ini="$(find_engine_ini || true)"
|
||||
[ -z "$ini" ] && { echo "[i] Engine.ini not found (launch the game once first); skipping menu-level win."; return; }
|
||||
if grep -qF "$MARK_BEGIN" "$ini"; then echo "[i] Engine.ini tweak already present."; return; fi
|
||||
cp -v "$ini" "$ini.bak-bg4uw"
|
||||
{ echo ""; echo "$MARK_BEGIN"; echo "[/Script/Engine.LocalPlayer]";
|
||||
echo "AspectRatioAxisConstraint=AspectRatio_MaintainYFOV"; echo "$MARK_END"; } >> "$ini"
|
||||
echo "[+] Engine.ini: added MaintainYFOV (fixes menus/lobby immediately; SUWSF covers missions)."
|
||||
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_engine_ini() {
|
||||
local ini; ini="$(find_engine_ini || true)"
|
||||
remove_ini() {
|
||||
local ini; ini="$(find_prefix_ini || true)"
|
||||
[ -z "$ini" ] && return
|
||||
if grep -qF "$MARK_BEGIN" "$ini"; then
|
||||
sed -i "/$(printf '%s' "$MARK_BEGIN" | sed 's/[][\/.*^$]/\\&/g')/,/$(printf '%s' "$MARK_END" | sed 's/[][\/.*^$]/\\&/g')/d" "$ini"
|
||||
echo "[+] Engine.ini: removed BG4 UltraWide block."
|
||||
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
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
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"; }
|
||||
[ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f"
|
||||
done
|
||||
remove_engine_ini
|
||||
echo "[+] Done. (game files untouched; nothing was ever modified in the exe)"
|
||||
}
|
||||
|
||||
if [ "${1:-}" = "uninstall" ]; then uninstall; exit 0; fi
|
||||
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_NAME"
|
||||
apply_engine_ini
|
||||
cp -v "$LOADER_SRC" "$GAMEDIR/$LOADER"
|
||||
apply_ini
|
||||
|
||||
echo
|
||||
echo "[+] Installed: SUWSF.asi, SUWSF.ini, $LOADER_NAME"
|
||||
echo
|
||||
echo "NEXT (Steam):"
|
||||
echo " 1. If the game does not apply the fix, add this to GB4's Steam"
|
||||
echo " Launch Options (right-click game > Properties > Launch Options):"
|
||||
echo " WINEDLLOVERRIDES=\"${LOADER_NAME%.dll}=n,b\" %command%"
|
||||
echo " 2. Set the in-game resolution to 5120x1440 (fullscreen)."
|
||||
echo " 3. Launch. Camera should widen (Hor+) instead of zooming in."
|
||||
echo
|
||||
echo "If nothing changes, the runtime AOB may differ for GB4. Run:"
|
||||
echo " python3 tools/dump_decrypted.py (while game is at main menu)"
|
||||
echo " python3 tools/aob.py (reports which patterns matched)"
|
||||
echo
|
||||
echo "To try a different loader name: LOADER_NAME=version.dll ./install.sh"
|
||||
echo "To remove everything: ./install.sh uninstall"
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user