diff --git a/CMakeLists.txt b/CMakeLists.txt index ec89636..009dd5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 5501266..7785959 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -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") { - $api = "https://api.github.com/repos/$Repo/releases/latest" - $rel = Invoke-RestMethod -Uri $api - $Tag = $rel.tag_name + # 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 diff --git a/scripts/install.sh b/scripts/install.sh index 3c2eab4..309ff62 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -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 -if [[ "$TAG" == "latest" ]]; then - 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; } +# --- 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 -echo "[*] Installing $REPO $TAG" -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 +# --- 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 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" diff --git a/src/platform/version_proxy.cpp b/src/platform/version_proxy.cpp new file mode 100644 index 0000000..9d283ee --- /dev/null +++ b/src/platform/version_proxy.cpp @@ -0,0 +1,18 @@ +#ifdef _WIN32 +#include +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 diff --git a/tools/check_offsets.py b/tools/check_offsets.py new file mode 100644 index 0000000..ea25b35 --- /dev/null +++ b/tools/check_offsets.py @@ -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))