After a reboot E33 kept crashing. The ClairObscurFix.log showed why the earlier 'reduced profile' never helped: config flags gate behaviour INSIDE the fix's hooks, but the .asi installs all its hooks (Level Load, Widgets, cutscene, PostProcess) regardless. So the crash surface was never reduced - only removing the fix stops it. The game added official ultrawide support in patch 1.2.3 and renders correctly at 5120x1440 natively, so the archived fix was pure liability. Removed it entirely and marked the entry native. - New METHOD=native: install.sh no-ops with a message, build/publish skip it. - Deleted vendor/ClairObscurFix and the tuned ini (unused, crash-causing). - README/THIRD_PARTY/CHANGELOG updated; ClairObscurFix recorded as historical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
199 lines
7.3 KiB
Bash
Executable File
199 lines
7.3 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
|
|
}
|
|
|
|
METHOD="${METHOD:-suwsf}"
|
|
|
|
if [ "$METHOD" = "native" ]; then
|
|
echo "[i] ${TITLE:-$GAME} supports ultrawide natively — no fix to install or remove."
|
|
echo " ${STATUS:-}"
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$ACTION" = "uninstall" ]; then
|
|
echo "[+] Uninstalling..."
|
|
if [ "$METHOD" = "external" ]; then
|
|
for f in $EXTERNAL_FILES; do [ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f"; done
|
|
elif [ "$METHOD" = "bepinex" ]; then
|
|
rm -vf "$GAMEDIR/winhttp.dll" "$GAMEDIR/doorstop_config.ini" "$GAMEDIR/.doorstop_version" \
|
|
"$GAMEDIR/changelog.txt" "$GAMEDIR/BepInEx/plugins/$PLUGIN_DLL"
|
|
# leave BepInEx/ itself: the user may have other mods installed
|
|
echo "[i] Left BepInEx/ in place (other mods may rely on it). Delete it manually if unused."
|
|
else
|
|
for f in SUWSF.asi SUWSF.ini dsound.dll dinput8.dll version.dll winmm.dll; do
|
|
[ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f"
|
|
done
|
|
fi
|
|
remove_ini
|
|
echo "[+] Done. No game file was ever modified."
|
|
exit 0
|
|
fi
|
|
|
|
# --- install ------------------------------------------------------------------
|
|
if [ "$METHOD" = "external" ]; then
|
|
SRC="$HERE/vendor/$VENDOR_PAYLOAD"
|
|
[ -d "$SRC" ] || { echo "[!] missing vendored payload: $SRC"; exit 1; }
|
|
cp -v "$SRC"/* "$GAMEDIR/" 2>/dev/null || true
|
|
# our tuned config wins over the upstream default
|
|
if [ -n "${EXTERNAL_CONFIG:-}" ] && [ -f "$GAMES_DIR/$GAME/$EXTERNAL_CONFIG" ]; then
|
|
cp -v "$GAMES_DIR/$GAME/$EXTERNAL_CONFIG" "$GAMEDIR/$EXTERNAL_CONFIG"
|
|
fi
|
|
elif [ "$METHOD" = "bepinex" ]; then
|
|
BEPZIP=$(ls "$HERE"/vendor/BepInEx_win_x64_*.zip 2>/dev/null | head -1)
|
|
PLUGIN="$GAMES_DIR/$GAME/$PLUGIN_DLL"
|
|
[ -f "$BEPZIP" ] || { echo "[!] missing BepInEx zip in vendor/"; exit 1; }
|
|
[ -f "$PLUGIN" ] || { echo "[!] missing prebuilt plugin: $PLUGIN"; exit 1; }
|
|
|
|
if [ -f "$GAMEDIR/winhttp.dll" ]; then
|
|
echo "[i] BepInEx already present; updating plugin only."
|
|
else
|
|
command -v unzip >/dev/null || { echo "[!] unzip required"; exit 1; }
|
|
unzip -o -q "$BEPZIP" -d "$GAMEDIR"
|
|
echo "[+] Installed BepInEx ($(basename "$BEPZIP"))"
|
|
fi
|
|
mkdir -p "$GAMEDIR/BepInEx/plugins"
|
|
cp -v "$PLUGIN" "$GAMEDIR/BepInEx/plugins/"
|
|
else
|
|
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"
|
|
fi
|
|
apply_ini
|
|
|
|
cat <<EOF
|
|
|
|
[+] Installed ($METHOD): $(case "$METHOD" in
|
|
bepinex) echo "BepInEx + $PLUGIN_DLL + $LOADER" ;;
|
|
external) echo "$EXTERNAL_FILES" ;;
|
|
*) echo "SUWSF.asi, SUWSF.ini, $LOADER" ;;
|
|
esac)
|
|
|
|
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.
|
|
|
|
Tuning (METHOD=bepinex games):
|
|
Config is generated on first launch at
|
|
<game>/BepInEx/config/*.cfg (edits apply live, ~2s, no restart)
|
|
Plugin log (proves whether it applied):
|
|
<game>/BepInEx/LogOutput.log
|
|
|
|
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
|