Files
gzw-eac-fix/scripts/fix.sh
2026-04-17 16:58:42 +02:00

190 lines
5.3 KiB
Bash

#!/usr/bin/env bash
set -uo pipefail
# set by setup.sh via @@TOKEN@@ substitution
STEAM_APP_ID="@@STEAM_APP_ID@@"
SERVICE_NAME="@@SERVICE_NAME@@"
INSTALL_DIR="@@INSTALL_DIR@@"
NOTIFY="@@NOTIFY@@"
LOG_MAX_LINES="@@LOG_MAX_LINES@@"
POLL_INTERVAL="@@POLL_INTERVAL@@"
GAME_SUBPATH="common/Gray Zone Warfare/GZW/Content/SKALLA/PrebuildWorldData/World/cache"
MANIFEST_NAME="appmanifest_${STEAM_APP_ID}.acf"
EAC_FILES=(
"0xb9af63cee2e43b6c_0x3cb3b3354fb31606.dat"
"0xaf497c273f87b6e4_0x7a22fc105639587d.dat"
)
LOG_FILE="$INSTALL_DIR/${SERVICE_NAME}.log"
STATE_FILE="$INSTALL_DIR/.last_known_state"
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m'
PREFIX="[${SERVICE_NAME}]"
mkdir -p "$INSTALL_DIR"
_ts() { date "+%Y-%m-%d %H:%M:%S"; }
{
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " [Gray Zone Warfare EAC Fix] - $(_ts)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
} >> "$LOG_FILE"
# Trim log to LOG_MAX_LINES
_lines=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
if (( _lines > LOG_MAX_LINES )); then
tail -n "$LOG_MAX_LINES" "$LOG_FILE" > "${LOG_FILE}.tmp" && mv "${LOG_FILE}.tmp" "$LOG_FILE"
fi
log_info() {
local msg="$1"
printf "${BLUE}${PREFIX}${NC} %s\n" "$msg"
echo "[$(_ts)] [INFO] $msg" >> "$LOG_FILE"
}
log_warn() {
local msg="$1"
printf "${BLUE}${PREFIX}${NC} ⚠ %s\n" "$msg"
echo "[$(_ts)] [WARN] $msg" >> "$LOG_FILE"
}
log_error() {
local msg="$1"
printf "${RED}${PREFIX} Error: %s${NC}\n" "$msg" >&2
echo "[$(_ts)] [ERROR] $msg" >> "$LOG_FILE"
}
die() { log_error "$1"; exit 1; }
_notify() {
[[ "$NOTIFY" != "true" ]] && return
command -v notify-send &>/dev/null || return
notify-send -a "$SERVICE_NAME" "$1" "$2" 2>/dev/null || true
}
find_steam_library() {
local bases=(
"$HOME/.local/share/Steam"
"$HOME/.steam/steam"
"$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam"
)
declare -A seen
local all=()
for base in "${bases[@]}"; do
[[ ! -d "$base/steamapps" ]] && continue
local real; real=$(realpath "$base" 2>/dev/null || echo "$base")
[[ "${seen[$real]+x}" ]] && continue
seen["$real"]=1
all+=("$base/steamapps")
local vdf="$base/steamapps/libraryfolders.vdf"
[[ ! -f "$vdf" ]] && continue
while IFS= read -r line; do
local p; p=$(awk -F'"' '/"path"/{print $4}' <<< "$line")
[[ -n "$p" && -d "$p/steamapps" ]] && all+=("$p/steamapps")
done < "$vdf"
done
[[ ${#all[@]} -eq 0 ]] && return 1
for lib in "${all[@]}"; do
[[ -d "$lib/$GAME_SUBPATH" ]] && echo "$lib" && return 0
done
return 1
}
STEAM_APPS=$(find_steam_library) || die "Gray Zone Warfare not found in any Steam library."
CACHE_DIR="$STEAM_APPS/$GAME_SUBPATH"
MANIFEST="$STEAM_APPS/$MANIFEST_NAME"
log_info "GZW found at: $CACHE_DIR"
# Build State Detection:
# Fingerprint = buildid + all InstalledDepots manifest IDs (sorted).
# Depot manifests rotate on every content update even if the buildid doesn't.
# If fingerprint matches last run, skip the fix - nothing was actually updated.
read_game_state() {
local acf="$1"
local buildid
buildid=$(grep -m1 '"buildid"' "$acf" | awk -F'"' '{print $4}')
local depots
depots=$(awk '
/"InstalledDepots"/ { in_depots=1; depth=0; next }
in_depots && /\{/ { depth++ }
in_depots && /\}/ { depth--; if (depth < 0) in_depots=0 }
in_depots && /"manifest"/ { gsub(/"/, ""); print $2 }
' "$acf" | sort | paste -sd ':')
[[ -z "$buildid" || -z "$depots" ]] && return 1
echo "${buildid}:${depots}"
}
[[ -f "$MANIFEST" ]] || die "Steam manifest not found: $MANIFEST"
CURRENT_STATE=$(read_game_state "$MANIFEST") \
|| die "Could not parse build ID or depot manifests from ACF."
LAST_STATE=$(cat "$STATE_FILE" 2>/dev/null || echo "")
if [[ "$CURRENT_STATE" == "$LAST_STATE" ]]; then
log_info "No update detected (state unchanged). Skipping."
exit 0
fi
if [[ -n "$LAST_STATE" ]]; then
log_info "Update detected."
log_info " Previous: $LAST_STATE"
log_info " Current: $CURRENT_STATE"
else
log_info "No previous state - running fix and recording baseline."
fi
# applying fix (remove -> regenerate -> lock)
_notify -i dialog-information "Applying EAC cache fix..."
log_info "Flushing disk before delete..."
sync
log_info "Removing EAC cache files..."
for f in "${EAC_FILES[@]}"; do
rm -f "$CACHE_DIR/$f"
log_info " Removed: $f"
done
log_info "Triggering Steam verify integrity (app $STEAM_APP_ID)..."
steam "steam://validate/$STEAM_APP_ID"
log_info "Waiting for Steam to restore files..."
for f in "${EAC_FILES[@]}"; do
while [[ ! -f "$CACHE_DIR/$f" ]]; do
sleep "$POLL_INTERVAL"
done
log_info " Restored: $f"
done
log_info "Flushing disk after restore..."
sync
log_info "Setting files read-only..."
for f in "${EAC_FILES[@]}"; do
chmod 400 "$CACHE_DIR/$f"
log_info " chmod 400: $f"
done
echo "$CURRENT_STATE" > "$STATE_FILE"
_notify -i dialog-information "EAC cache fix applied."
log_info "Done."