- version.dll proxy (6 exports GetFileVersionInfoA/W etc, runtime forwarding to C:\Windows\System32\version.dll, less flagged than winhttp) built via CMake version SHARED - install.sh: platform detector (Linux/Darwin/Windows), --install/--uninstall/--update/--repair, detects Steam via libraryfolders.vdf, pulls version.dll+winhttp.dll+DevourUnlocker.dll per OS, backs up, patches collectables.json via devour_unlocker.py, handles Forgejo vs GitHub API, no BepInEx required but BepInEx still built as fallback, macOS note (no GameAssembly.dylib available) - install.ps1: same for Windows (Find-GameDir, backup, version.dll primary) - check_offsets.py: verifies GameAssembly sha1 and IsRobeUpdated/UpdateLockedPets still in global-metadata, warns if GameAssembly changed since last install
147 lines
7.9 KiB
Bash
Executable file
147 lines
7.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# DevourUnlocker — one-click per-platform installer (detects OS, pulls correct files, supports uninstall/update/repair)
|
|
# Primary proxy is version.dll (less flagged than winhttp.dll) — winhttp kept for compat
|
|
# Usage: bash install.sh [--install] [--uninstall] [--update|--repair] [--tag v2.1.0] [--game-dir /path] [--repo ad3laid3/DevourUnlocker]
|
|
|
|
REPO="${REPO:-ad3laid3/DevourUnlocker}"
|
|
TAG="${TAG:-latest}"
|
|
GAME_DIR=""
|
|
MODE="install"
|
|
|
|
detect_os() {
|
|
case "$(uname -s)" in
|
|
Linux*) echo "linux" ;;
|
|
Darwin*) echo "macos" ;;
|
|
MINGW*|MSYS*|CYGWIN*) echo "windows" ;;
|
|
*) echo "linux" ;;
|
|
esac
|
|
}
|
|
|
|
detect_game_dir() {
|
|
local cands=(
|
|
"$HOME/.local/share/Steam/steamapps/common/Devour"
|
|
"$HOME/.steam/steam/steamapps/common/Devour"
|
|
"/mnt/*/SteamLibrary/steamapps/common/Devour"
|
|
"C:/Program Files (x86)/Steam/steamapps/common/Devour"
|
|
)
|
|
for c in "${cands[@]}"; do for e in $c; do [[ -f "$e/DEVOUR.exe" ]] && { echo "$e"; return 0; }; done; done
|
|
local vdf="$HOME/.local/share/Steam/steamapps/libraryfolders.vdf"
|
|
if [[ -f "$vdf" ]]; then grep -o '"path"[[:space:]]*"[^"]*"' "$vdf" | cut -d'"' -f4 | while read -r p; do [[ -f "$p/steamapps/common/Devour/DEVOUR.exe" ]] && { echo "$p/steamapps/common/Devour"; return 0; }; done; fi
|
|
return 1
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--install) MODE="install"; shift;;
|
|
--uninstall) MODE="uninstall"; shift;;
|
|
--update|--repair) MODE="update"; shift;;
|
|
--tag) TAG="$2"; shift 2;;
|
|
--game-dir) GAME_DIR="$2"; shift 2;;
|
|
--repo) REPO="$2"; shift 2;;
|
|
*) echo "Unknown arg $1 (try --install|--uninstall|--update)"; exit 1;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$GAME_DIR" ]]; then GAME_DIR="$(detect_game_dir || true)"; fi
|
|
if [[ -z "$GAME_DIR" || ! -f "$GAME_DIR/DEVOUR.exe" ]]; then echo "[!] Could not find Devour install. Use --game-dir /path/to/Devour"; exit 1; fi
|
|
OS=$(detect_os)
|
|
echo "[*] OS: $OS Game dir: $GAME_DIR Mode: $MODE Repo: $REPO Tag: $TAG"
|
|
|
|
# --- UNINSTALL ---
|
|
if [[ "$MODE" == "uninstall" ]]; then
|
|
echo "[*] Uninstalling (removing version.dll/winhttp.dll + Cloud patch)..."
|
|
rm -f "$GAME_DIR/version.dll" "$GAME_DIR/winhttp.dll" "$GAME_DIR/DevourUnlocker.dll" "$GAME_DIR/DevourUnlocker.json" "$GAME_DIR/DevourUnlocker.log"
|
|
[[ -f "$GAME_DIR/version.dll.bak" ]] && mv "$GAME_DIR/version.dll.bak" "$GAME_DIR/version.dll" && echo "[*] Restored version.dll.bak"
|
|
[[ -f "$GAME_DIR/winhttp.dll.bak" ]] && mv "$GAME_DIR/winhttp.dll.bak" "$GAME_DIR/winhttp.dll" && echo "[*] Restored winhttp.dll.bak"
|
|
# Revert collectables.json to Steam Cloud (remove our superset)
|
|
python3 -c "
|
|
import pathlib, json
|
|
for p in pathlib.Path.home().glob('.local/share/Steam/userdata/*/1274570/remote/collectables.json'):
|
|
try:
|
|
j=json.loads(p.read_text())
|
|
# clear to minimal (will be re-fetched from cloud on next launch)
|
|
p.write_text('{}')
|
|
print(f'[+] Cleared {p}')
|
|
except: pass
|
|
"
|
|
echo "[*] Uninstalled. Remove from Steam → Devour → Properties → Launch Options: WINEDLLOVERRIDES if you added it."
|
|
exit 0
|
|
fi
|
|
|
|
# --- UPDATE / REPAIR: redownload + verify GameAssembly hasn't shifted ---
|
|
if [[ "$MODE" == "update" ]]; then
|
|
echo "[*] Repair: checking GameAssembly hash + metadata offsets..."
|
|
GA="$GAME_DIR/GameAssembly.dll"
|
|
if [[ -f "$GA" ]]; then
|
|
sha=$(sha1sum "$GA" | cut -d' ' -f1)
|
|
echo "[*] GameAssembly sha1 $sha"
|
|
# Compare to last installed sha stored in DevourUnlocker.json if present
|
|
if [[ -f "$GAME_DIR/DevourUnlocker.json" ]]; then
|
|
old=$(python3 -c "import json,pathlib; print(json.loads(open('$GAME_DIR/DevourUnlocker.json').read()).get('gameAssemblySha',''))" 2>/dev/null || echo "")
|
|
if [[ -n "$old" && "$old" != "$sha" ]]; then
|
|
echo "[!] GameAssembly changed since last install (game updated) — will re-resolve offsets"
|
|
fi
|
|
fi
|
|
fi
|
|
# Fall through to install (redownload)
|
|
fi
|
|
|
|
# --- INSTALL ---
|
|
if [[ "$TAG" == "latest" ]]; then
|
|
# Forgejo (git.mithraic.cloud) uses /api/v1/repos/:owner/:repo/releases/latest
|
|
API="https://git.mithraic.cloud/api/v1/repos/$REPO/releases/latest"
|
|
if curl -s "$API" | grep -q "tag_name"; then
|
|
TAG=$(curl -s "$API" | grep -o '"tag_name"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4)
|
|
else
|
|
API="https://api.github.com/repos/$REPO/releases/latest"
|
|
TAG=$(curl -s "$API" | grep -o '"tag_name"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4)
|
|
fi
|
|
[[ -z "$TAG" ]] && { echo "[!] Could not resolve latest tag from $REPO"; exit 1; }
|
|
fi
|
|
echo "[*] Installing $REPO $TAG for $OS"
|
|
|
|
# Per-platform artifact names
|
|
if [[ "$OS" == "windows" ]]; then ZIP="DevourUnlocker_version_${TAG}.zip"; URL="https://git.mithraic.cloud/$REPO/releases/download/$TAG/$ZIP"; ALT_URL="https://github.com/$REPO/releases/download/$TAG/$ZIP"
|
|
elif [[ "$OS" == "macos" ]]; then echo "[!] macOS no native GameAssembly.dylib available — will install Linux/Windows version for Proton/Wine. Use --game-dir to override."; ZIP="DevourUnlocker_winhttp_${TAG}.zip"; URL="https://git.mithraic.cloud/$REPO/releases/download/$TAG/$ZIP"; ALT_URL="https://github.com/$REPO/releases/download/$TAG/$ZIP"
|
|
else ZIP="DevourUnlocker_version_${TAG}.zip"; URL="https://git.mithraic.cloud/$REPO/releases/download/$TAG/$ZIP"; ALT_URL="https://github.com/$REPO/releases/download/$TAG/$ZIP"; fi
|
|
|
|
TMP="$(mktemp -d)"
|
|
echo "[*] Downloading $URL (fallback $ALT_URL)"
|
|
if ! curl -L -o "$TMP/pkg.zip" "$URL"; then curl -L -o "$TMP/pkg.zip" "$ALT_URL" || { echo "[!] Download failed — check REPO/TAG"; echo " Fallback: build locally: cmake -B build && cmake --build build"; exit 1; }; fi
|
|
unzip -o "$TMP/pkg.zip" -d "$TMP" >/dev/null
|
|
|
|
# Backup existing version/winhttp if not ours (check for DevourUnlocker string)
|
|
for dll in version.dll winhttp.dll; do
|
|
if [[ -f "$GAME_DIR/$dll" ]]; then
|
|
if ! grep -q "DevourUnlocker" "$GAME_DIR/$dll" 2>/dev/null; then
|
|
cp "$GAME_DIR/$dll" "$GAME_DIR/$dll.bak" && echo "[*] Backed up $dll → $dll.bak"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Prefer version.dll (less flagged) — copy both for compat
|
|
if [[ -f "$TMP/version.dll" ]]; then cp -v "$TMP/version.dll" "$GAME_DIR/version.dll"; echo "[*] Installed version.dll (primary, less detectable)"; fi
|
|
if [[ -f "$TMP/winhttp.dll" ]]; then cp -v "$TMP/winhttp.dll" "$GAME_DIR/winhttp.dll"; echo "[*] Installed winhttp.dll (compat)"; fi
|
|
cp -v "$TMP/DevourUnlocker.dll" "$GAME_DIR/DevourUnlocker.dll" 2>/dev/null || true
|
|
# Record GameAssembly hash for future update checks
|
|
GA_SHA=$(sha1sum "$GAME_DIR/GameAssembly.dll" 2>/dev/null | cut -d' ' -f1 || echo "")
|
|
if [[ -f "$TMP/DevourUnlocker.json" ]]; then
|
|
python3 -c "import json; j=json.load(open('$TMP/DevourUnlocker.json')); j['gameAssemblySha']='$GA_SHA'; open('$GAME_DIR/DevourUnlocker.json','w').write(json.dumps(j,indent=2))"
|
|
else
|
|
python3 -c "import json; open('$GAME_DIR/DevourUnlocker.json','w').write(json.dumps({'unlockRobes':True,'unlockCharacters':True,'unlockPerks':True,'unlockPets':True,'unlockFlashlights':True,'unlockEmotes':True,'unlockOutfits':True,'gameAssemblySha':'$GA_SHA'},indent=2))"
|
|
fi
|
|
|
|
# Patch Steam Cloud collectables.json via python helper (uses proper game calls: HasCollectedAll* checks)
|
|
echo "[*] Patching Steam Cloud collectables.json (proper game call: HasCollectedAllBarbedWires etc)..."
|
|
python3 "$(dirname "$0")/../standalone/devour_unlocker.py" unlock --all 2>&1 | tail -n 20 || echo "[!] Could not patch collectables.json — will still unlock via runtime hook"
|
|
|
|
echo "[*] Installed to $GAME_DIR"
|
|
if [[ "$OS" == "linux" ]]; then
|
|
echo "[*] Linux/Proton: version.dll is auto-loaded beside DEVOUR.exe (no WINEDLLOVERRIDES needed on native Windows, but on Proton set Steam → Devour → Properties → Launch Options: WINEDLLOVERRIDES=\"version=n,b\" %command% or just use scripts/launch.sh)"
|
|
echo "[*] Or run: bash $(dirname "$0")/launch.sh (auto sets WINEDLLOVERRIDES and launches via Proton)"
|
|
else
|
|
echo "[*] Windows: version.dll beside DEVOUR.exe is auto-loaded — no launch option needed. Just launch Devour."
|
|
fi
|
|
echo "[*] Verify: python $(dirname "$0")/../standalone/devour_unlocker.py diagnose"
|
|
rm -rf "$TMP"
|