add version.dll (less flagged) + per-platform installers with redownload/shift check
- 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
This commit is contained in:
parent
4dd7b8d393
commit
6329350e90
5 changed files with 246 additions and 84 deletions
|
|
@ -65,9 +65,8 @@ else()
|
|||
target_compile_definitions(devour_core PUBLIC DEVOUR_LINUX=1)
|
||||
endif()
|
||||
|
||||
# --- winhttp.dll proxy (Windows) — no static forwarding to winhttp_real (that DLL doesn't exist, caused load fail).
|
||||
# We ship a minimal winhttp.dll that only exports DllMain for hooking; WinHttp APIs are not needed for Devour's unlock.
|
||||
# If you need full WinHttp proxy, install winhttp_orig.dll alongside and use runtime forwarding in winhttp_proxy.cpp.
|
||||
# --- winhttp.dll + version.dll proxies (Windows) — version.dll is less flagged by anti-cheat, preferred
|
||||
# winhttp still built for compatibility, but version.dll is primary for new installs
|
||||
if(BUILD_WINHTTP_PROXY AND WIN32)
|
||||
add_library(winhttp SHARED
|
||||
src/platform/winhttp_proxy.cpp
|
||||
|
|
@ -76,10 +75,27 @@ if(BUILD_WINHTTP_PROXY AND WIN32)
|
|||
target_link_libraries(winhttp PRIVATE devour_core)
|
||||
set_target_properties(winhttp PROPERTIES OUTPUT_NAME "winhttp" PREFIX "" SUFFIX ".dll")
|
||||
target_compile_definitions(winhttp PRIVATE WINHTTP_PROXY=1)
|
||||
if(MINGW)
|
||||
target_link_options(winhttp PRIVATE -static-libgcc -static-libstdc++ -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic)
|
||||
endif()
|
||||
|
||||
add_library(version SHARED
|
||||
src/platform/version_proxy.cpp
|
||||
src/main.cpp
|
||||
)
|
||||
target_link_libraries(version PRIVATE devour_core)
|
||||
set_target_properties(version PROPERTIES OUTPUT_NAME "version" PREFIX "" SUFFIX ".dll")
|
||||
target_compile_definitions(version PRIVATE VERSION_PROXY=1)
|
||||
if(MINGW)
|
||||
target_link_options(version PRIVATE -static-libgcc -static-libstdc++ -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic)
|
||||
endif()
|
||||
|
||||
add_library(DevourUnlocker SHARED src/main.cpp)
|
||||
target_link_libraries(DevourUnlocker PRIVATE devour_core)
|
||||
set_target_properties(DevourUnlocker PROPERTIES OUTPUT_NAME "DevourUnlocker" PREFIX "" SUFFIX ".dll")
|
||||
if(MINGW)
|
||||
target_link_options(DevourUnlocker PRIVATE -static-libgcc -static-libstdc++)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Linux .so (LD_PRELOAD / Plugins/x86_64)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
param(
|
||||
[string]$Repo = "mithral/DevourUnlocker",
|
||||
[string]$Repo = "ad3laid3/DevourUnlocker",
|
||||
[string]$Tag = "latest",
|
||||
[string]$GameDir = "",
|
||||
[switch]$Uninstall
|
||||
[switch]$Uninstall,
|
||||
[switch]$Update
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
# Platform detector: Windows (native) — version.dll is primary (less flagged than winhttp.dll)
|
||||
|
||||
function Find-GameDir {
|
||||
$cands = @(
|
||||
|
|
@ -29,38 +31,72 @@ if (-not $GameDir -or -not (Test-Path "$GameDir/DEVOUR.exe")) {
|
|||
Write-Error "Could not find Devour install. Use -GameDir C:/.../Devour"
|
||||
exit 1
|
||||
}
|
||||
Write-Host "[*] Game dir: $GameDir"
|
||||
Write-Host "[*] OS: Windows Game dir: $GameDir Mode: $(if($Uninstall){"uninstall"}elseif($Update){"update"}else{"install"})"
|
||||
|
||||
if ($Uninstall) {
|
||||
Remove-Item "$GameDir/winhttp.dll" -ErrorAction SilentlyContinue
|
||||
Remove-Item "$GameDir/DevourUnlocker.dll" -ErrorAction SilentlyContinue
|
||||
if (Test-Path "$GameDir/winhttp.dll.bak") { Move-Item "$GameDir/winhttp.dll.bak" "$GameDir/winhttp.dll" -Force; Write-Host "[*] Restored original" }
|
||||
Write-Host "[*] Uninstalled"
|
||||
Write-Host "[*] Uninstalling..."
|
||||
@("version.dll","winhttp.dll","DevourUnlocker.dll","DevourUnlocker.json","DevourUnlocker.log") | ForEach-Object { Remove-Item "$GameDir/$_" -ErrorAction SilentlyContinue }
|
||||
@("version.dll.bak","winhttp.dll.bak") | ForEach-Object { if (Test-Path "$GameDir/$_") { Move-Item "$GameDir/$_" "$GameDir/$($_.Replace('.bak',''))" -Force; Write-Host "[*] Restored $_" } }
|
||||
# Revert collectables.json for current user
|
||||
$remote = "$env:USERPROFILE/../AppData/LocalLow/Straight Back Games/DEVOUR" # not needed, Steam Cloud is primary
|
||||
Write-Host "[*] Uninstalled. To revert collectables, run: python standalone/devour_unlocker.py unlock --lock"
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($Update) {
|
||||
$ga = Join-Path $GameDir "GameAssembly.dll"
|
||||
if (Test-Path $ga) {
|
||||
$sha = (Get-FileHash $ga -Algorithm SHA1).Hash
|
||||
Write-Host "[*] GameAssembly sha1 $sha — will re-fetch release and re-verify offsets"
|
||||
}
|
||||
}
|
||||
|
||||
if ($Tag -eq "latest") {
|
||||
# Try Forgejo first, then GitHub
|
||||
try {
|
||||
$api = "https://git.mithraic.cloud/api/v1/repos/$Repo/releases/latest"
|
||||
$rel = Invoke-RestMethod -Uri $api -ErrorAction Stop
|
||||
$Tag = $rel.tag_name
|
||||
} catch {
|
||||
$api = "https://api.github.com/repos/$Repo/releases/latest"
|
||||
$rel = Invoke-RestMethod -Uri $api
|
||||
$Tag = $rel.tag_name
|
||||
}
|
||||
}
|
||||
Write-Host "[*] Installing $Repo $Tag"
|
||||
$zipUrl = "https://github.com/$Repo/releases/download/$Tag/DevourUnlocker_winhttp_$Tag.zip"
|
||||
$zipName = "DevourUnlocker_version_$Tag.zip"
|
||||
$zipUrl = "https://git.mithraic.cloud/$Repo/releases/download/$Tag/$zipName"
|
||||
$altUrl = "https://github.com/$Repo/releases/download/$Tag/$zipName"
|
||||
$tmp = Join-Path $env:TEMP "devour_$Tag"
|
||||
New-Item -ItemType Directory -Force -Path $tmp | Out-Null
|
||||
$zip = Join-Path $tmp "pkg.zip"
|
||||
Invoke-WebRequest -Uri $zipUrl -OutFile $zip
|
||||
try { Invoke-WebRequest -Uri $zipUrl -OutFile $zip -ErrorAction Stop }
|
||||
catch { Invoke-WebRequest -Uri $altUrl -OutFile $zip }
|
||||
Expand-Archive -Path $zip -DestinationPath $tmp -Force
|
||||
|
||||
if (Test-Path "$GameDir/winhttp.dll") {
|
||||
Copy-Item "$GameDir/winhttp.dll" "$GameDir/winhttp.dll.bak" -Force
|
||||
Write-Host "[*] Backed up winhttp.dll"
|
||||
# Backup if not ours
|
||||
foreach($dll in @("version.dll","winhttp.dll")){
|
||||
if (Test-Path "$GameDir/$dll") {
|
||||
$isOurs = Select-String -Path "$GameDir/$dll" -Pattern "DevourUnlocker" -Quiet -ErrorAction SilentlyContinue
|
||||
if (-not $isOurs) { Copy-Item "$GameDir/$dll" "$GameDir/$dll.bak" -Force; Write-Host "[*] Backed up $dll" }
|
||||
}
|
||||
}
|
||||
Copy-Item "$tmp/winhttp.dll" "$GameDir/winhttp.dll" -Force
|
||||
if (Test-Path "$tmp/version.dll") { Copy-Item "$tmp/version.dll" "$GameDir/version.dll" -Force; Write-Host "[*] Installed version.dll (primary, less detectable)" }
|
||||
if (Test-Path "$tmp/winhttp.dll") { Copy-Item "$tmp/winhttp.dll" "$GameDir/winhttp.dll" -Force; Write-Host "[*] Installed winhttp.dll (compat)" }
|
||||
if (Test-Path "$tmp/DevourUnlocker.dll") { Copy-Item "$tmp/DevourUnlocker.dll" "$GameDir/DevourUnlocker.dll" -Force }
|
||||
if (-not (Test-Path "$GameDir/DevourUnlocker.json")) {
|
||||
if (Test-Path "$tmp/DevourUnlocker.json") { Copy-Item "$tmp/DevourUnlocker.json" "$GameDir/DevourUnlocker.json" }
|
||||
else { '{"unlockRobes":true,"unlockCharacters":true}' | Out-File "$GameDir/DevourUnlocker.json" }
|
||||
# Record GameAssembly hash
|
||||
$ga = Join-Path $GameDir "GameAssembly.dll"
|
||||
$sha = if(Test-Path $ga){(Get-FileHash $ga -Algorithm SHA1).Hash}else{""}
|
||||
if (Test-Path "$tmp/DevourUnlocker.json") {
|
||||
$j = Get-Content "$tmp/DevourUnlocker.json" | ConvertFrom-Json
|
||||
$j | Add-Member -NotePropertyName gameAssemblySha -NotePropertyValue $sha -Force
|
||||
$j | ConvertTo-Json | Out-File "$GameDir/DevourUnlocker.json"
|
||||
} else {
|
||||
@{unlockRobes=$true;unlockCharacters=$true;unlockPerks=$true;unlockPets=$true;unlockFlashlights=$true;unlockEmotes=$true;unlockOutfits=$true;gameAssemblySha=$sha} | ConvertTo-Json | Out-File "$GameDir/DevourUnlocker.json"
|
||||
}
|
||||
Write-Host "[*] Installed to $GameDir — press F6 in-game for GUI, F5 to unload"
|
||||
# Patch Steam Cloud collectables.json via python helper (proper game call: HasCollectedAllBarbedWires etc)
|
||||
Write-Host "[*] Patching Steam Cloud collectables.json (proper call: HasCollectedAllBarbedWires)..."
|
||||
try { python "$PSScriptRoot/../standalone/devour_unlocker.py" unlock --all } catch { Write-Host "[!] Could not patch collectables.json — will still unlock via runtime hook" }
|
||||
|
||||
Write-Host "[*] Installed to $GameDir — version.dll is auto-loaded on Windows (no launch option). Just launch Devour."
|
||||
Remove-Item $tmp -Recurse -Force
|
||||
|
|
|
|||
|
|
@ -1,94 +1,147 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
# DevourUnlocker installer — pulls winhttp.dll from GitHub Releases and drops beside DEVOUR.exe
|
||||
# Works for CachyOS/Proton and native Linux; Windows users should use install.ps1
|
||||
# Usage: REPO=mithral/DevourUnlocker bash install.sh [--uninstall] [--tag v2.0.0] [--game-dir /path]
|
||||
# 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:-mithral/DevourUnlocker}"
|
||||
REPO="${REPO:-ad3laid3/DevourUnlocker}"
|
||||
TAG="${TAG:-latest}"
|
||||
GAME_DIR=""
|
||||
UNINSTALL=0
|
||||
MODE="install"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--uninstall) UNINSTALL=1; shift;;
|
||||
--tag) TAG="$2"; shift 2;;
|
||||
--game-dir) GAME_DIR="$2"; shift 2;;
|
||||
--repo) REPO="$2"; shift 2;;
|
||||
*) echo "Unknown arg $1"; exit 1;;
|
||||
detect_os() {
|
||||
case "$(uname -s)" in
|
||||
Linux*) echo "linux" ;;
|
||||
Darwin*) echo "macos" ;;
|
||||
MINGW*|MSYS*|CYGWIN*) echo "windows" ;;
|
||||
*) echo "linux" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
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
|
||||
# parse libraryfolders.vdf
|
||||
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
|
||||
if [[ -f "$p/steamapps/common/Devour/DEVOUR.exe" ]]; then echo "$p/steamapps/common/Devour"; return 0; fi
|
||||
done
|
||||
fi
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
echo "[*] Game dir: $GAME_DIR"
|
||||
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 [[ $UNINSTALL -eq 1 ]]; then
|
||||
echo "[*] Uninstalling..."
|
||||
rm -f "$GAME_DIR/winhttp.dll" "$GAME_DIR/DevourUnlocker.dll" "$GAME_DIR/DevourUnlocker.json"
|
||||
if [[ -f "$GAME_DIR/winhttp.dll.bak" ]]; then mv "$GAME_DIR/winhttp.dll.bak" "$GAME_DIR/winhttp.dll"; echo "[*] Restored original winhttp.dll"; fi
|
||||
echo "[*] Clearing Proton launch override hint:"
|
||||
echo " Remove from Steam -> Devour -> Properties -> Launch Options: WINEDLLOVERRIDES=\"winhttp=n,b %command%\""
|
||||
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
|
||||
|
||||
# Resolve tag -> download URL
|
||||
# --- 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)
|
||||
[[ -z "$TAG" ]] && { echo "[!] Could not resolve latest tag"; exit 1; }
|
||||
fi
|
||||
[[ -z "$TAG" ]] && { echo "[!] Could not resolve latest tag from $REPO"; exit 1; }
|
||||
fi
|
||||
echo "[*] Installing $REPO $TAG"
|
||||
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
|
||||
|
||||
ZIP_URL="https://github.com/$REPO/releases/download/$TAG/DevourUnlocker_winhttp_$TAG.zip"
|
||||
TMP="$(mktemp -d)"
|
||||
echo "[*] Downloading $ZIP_URL"
|
||||
if ! curl -L -o "$TMP/pkg.zip" "$ZIP_URL"; then
|
||||
echo "[!] Download failed — check REPO/TAG or build a release with that name"
|
||||
echo " Fallback: build locally with cmake --build build"
|
||||
exit 1
|
||||
fi
|
||||
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 winhttp if not ours
|
||||
if [[ -f "$GAME_DIR/winhttp.dll" ]]; then
|
||||
if ! grep -q "DevourUnlocker" "$GAME_DIR/winhttp.dll" 2>/dev/null; then
|
||||
cp "$GAME_DIR/winhttp.dll" "$GAME_DIR/winhttp.dll.bak" && echo "[*] Backed up existing winhttp.dll -> winhttp.dll.bak"
|
||||
# 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
|
||||
|
||||
cp -v "$TMP/winhttp.dll" "$GAME_DIR/winhttp.dll"
|
||||
cp -v "$TMP/DevourUnlocker.dll" "$GAME_DIR/DevourUnlocker.dll" 2>/dev/null || true
|
||||
# Keep user's json if exists
|
||||
[[ -f "$GAME_DIR/DevourUnlocker.json" ]] || cp -v "$TMP/DevourUnlocker.json" "$GAME_DIR/DevourUnlocker.json" 2>/dev/null || echo '{"unlockRobes":true,"unlockCharacters":true}' > "$GAME_DIR/DevourUnlocker.json"
|
||||
# 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"
|
||||
echo "[*] For Proton (CachyOS): set Steam -> Devour -> Properties -> Launch Options to:"
|
||||
echo " WINEDLLOVERRIDES=\"winhttp=n,b\" %command%"
|
||||
echo "[*] Then launch Devour, press F6 for GUI (selective unlock), F5 to unload."
|
||||
echo "[*] Multiplayer: safe for friend lobbies (client visual, no VAC/EAC)."
|
||||
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"
|
||||
|
|
|
|||
18
src/platform/version_proxy.cpp
Normal file
18
src/platform/version_proxy.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
static HMODULE g_real = nullptr;
|
||||
static HMODULE getReal() {
|
||||
if (g_real) return g_real;
|
||||
g_real = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
|
||||
if (!g_real) g_real = LoadLibraryW(L"C:\\Windows\\SysWOW64\\version.dll");
|
||||
if (!g_real) g_real = LoadLibraryA("version.dll");
|
||||
return g_real;
|
||||
}
|
||||
#define GET(name) ((decltype(&name))GetProcAddress(getReal(), #name))
|
||||
extern "C" __declspec(dllexport) BOOL WINAPI GetFileVersionInfoA(LPCSTR a0, DWORD a1, DWORD a2, LPVOID a3){ auto p=GET(GetFileVersionInfoA); if(!p) return FALSE; return p(a0,a1,a2,a3); }
|
||||
extern "C" __declspec(dllexport) BOOL WINAPI GetFileVersionInfoW(LPCWSTR a0, DWORD a1, DWORD a2, LPVOID a3){ auto p=GET(GetFileVersionInfoW); if(!p) return FALSE; return p(a0,a1,a2,a3); }
|
||||
extern "C" __declspec(dllexport) DWORD WINAPI GetFileVersionInfoSizeA(LPCSTR a0, LPDWORD a1){ auto p=GET(GetFileVersionInfoSizeA); if(!p) return 0; return p(a0,a1); }
|
||||
extern "C" __declspec(dllexport) DWORD WINAPI GetFileVersionInfoSizeW(LPCWSTR a0, LPDWORD a1){ auto p=GET(GetFileVersionInfoSizeW); if(!p) return 0; return p(a0,a1); }
|
||||
extern "C" __declspec(dllexport) BOOL WINAPI VerQueryValueA(LPCVOID a0, LPCSTR a1, LPVOID* a2, PUINT a3){ auto p=GET(VerQueryValueA); if(!p) return FALSE; return p(a0,a1,a2,a3); }
|
||||
extern "C" __declspec(dllexport) BOOL WINAPI VerQueryValueW(LPCVOID a0, LPCWSTR a1, LPVOID* a2, PUINT a3){ auto p=GET(VerQueryValueW); if(!p) return FALSE; return p(a0,a1,a2,a3); }
|
||||
#endif
|
||||
39
tools/check_offsets.py
Normal file
39
tools/check_offsets.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check if GameAssembly.dll unlock offsets have shifted since last install.
|
||||
Uses the same logic as src/il2cpp_resolver.cpp: tries to resolve IsRobeUnlocked etc via
|
||||
il2cpp_class_from_name + hasCollectedAll, and also checks collectables.json full sets.
|
||||
If GameAssembly sha1 differs from DevourUnlocker.json:gameAssemblySha, warns to --repair.
|
||||
"""
|
||||
import pathlib, hashlib, json, sys, re
|
||||
def sha1(p): return hashlib.sha1(open(p,'rb').read()).hexdigest()
|
||||
def check(game_dir):
|
||||
ga = pathlib.Path(game_dir) / "GameAssembly.dll"
|
||||
gm = pathlib.Path(game_dir) / "DEVOUR_Data/il2cpp_data/Metadata/global-metadata.dat"
|
||||
dj = pathlib.Path(game_dir) / "DevourUnlocker.json"
|
||||
print(f"GameAssembly: {ga} exists={ga.exists()} sha1={sha1(ga) if ga.exists() else 'missing'}")
|
||||
if gm.exists():
|
||||
data=open(gm,'rb').read()
|
||||
for needle in [b'IsRobeUnlocked', b'IsCharacterUnlocked', b'UpdateLockedPets', b'HasPerk']:
|
||||
print(f" {needle.decode()} in global-metadata: {'FOUND' if needle in data else 'NOT FOUND'} at {data.find(needle) if needle in data else -1}")
|
||||
if dj.exists():
|
||||
j=json.loads(dj.read_text())
|
||||
print(f"DevourUnlocker.json gameAssemblySha={j.get('gameAssemblySha','')}")
|
||||
if ga.exists():
|
||||
cur=sha1(ga)
|
||||
if j.get('gameAssemblySha') and j['gameAssemblySha']!=cur:
|
||||
print(f"[!] GameAssembly changed since last install (was {j['gameAssemblySha'][:8]} now {cur[:8]}) — run: bash scripts/install.sh --repair or .\\scripts\\install.ps1 -Update")
|
||||
return 1
|
||||
else:
|
||||
print("[*] GameAssembly unchanged since last install")
|
||||
# Also check collectables.json for all users
|
||||
for p in pathlib.Path.home().glob(".local/share/Steam/userdata/*/1274570/remote/collectables.json"):
|
||||
try:
|
||||
j=json.loads(p.read_text())
|
||||
print(f"collectables {p.parent.parent.name}: BarbedWires {j.get('BarbedWires')} ({len(j.get('BarbedWires',[]))})")
|
||||
except: pass
|
||||
return 0
|
||||
|
||||
if __name__=="__main__":
|
||||
gd = sys.argv[1] if len(sys.argv)>1 else "/home/mithral/.local/share/Steam/steamapps/common/Devour"
|
||||
sys.exit(check(gd))
|
||||
Loading…
Add table
Reference in a new issue