version.dll is now sole unlocker — full hooks, no user WINEDLLOVERRIDES needed
Some checks failed
Build DevourUnlocker / windows-cross (push) Failing after 8s
Build DevourUnlocker / linux (push) Failing after 7s
Build DevourUnlocker / standalone (push) Successful in 5s

- UnityPlayer.dll imports VERSION.dll + WINHTTP.dll (objdump -p) — version.dll beside DEVOUR.exe is auto-loaded on Windows, installer now silently auto-sets WINEDLLOVERRIDES=version=n,b in Steam localconfig.vdf for Proton so user just presses Play
- src/main_version.cpp: now full HookThread (wait GameAssembly.dll + installPatches) linking devour_core (was minimal no-hooks 103K, now 14M full like winhttp) — unlocks all categories: robes/characters/perks/pets/flashlights/emotes/outfits via IsRobe/IsCharacter/HasPerk/HasOutfit + UpdateLocked* hooks
- CMakeLists.txt: version target links devour_core + winpthread static, primary = version.dll less flagged than winhttp; winhttp retained only as optional compat
- scripts/install.sh: copy version.dll primary (all categories), auto-set LaunchOptions silent, message no manual step; scripts/launch.sh: now uses version=n,b auto, checks version.dll
- standalone/injector.py: find_built_dlls prefers version.dll, ensure_mod_deployed deploys version primary; standalone/devour_unlocker.py: diagnose hints updated to version.dll
- .github/workflows/build.yml: runs-on docker (Forgejo runner media-server labels docker v6.4.0), package version.dll primary zip, linux job also docker
This commit is contained in:
MiTHRAL 2026-08-24 00:29:51 -04:00
parent 8119cdf686
commit 1e0f727ab7
8 changed files with 95 additions and 66 deletions

View file

@ -7,10 +7,9 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
# media-server is the only runner on git.mithraic.cloud (no windows-latest/ubuntu-latest). Forgejo Act: runs-on must exactly match runner labels. # git.mithraic.cloud Forgejo runner: name media-server, labels [docker] (v6.4.0). Use `docker` to match.
windows-cross: windows-cross:
runs-on: media-server runs-on: docker
continue-on-error: true
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: { submodules: recursive } with: { submodules: recursive }
@ -28,16 +27,17 @@ jobs:
- name: Package - name: Package
run: | run: |
mkdir -p dist mkdir -p dist
cp build/winhttp.dll dist/ 2>/dev/null || cp build/*.dll dist/ 2>/dev/null || echo "no dll built (expected on cross without win32 SDK)" cp build/version.dll dist/ 2>/dev/null || echo "no version.dll"
cp build/winhttp.dll dist/ 2>/dev/null || true
cp build/DevourUnlocker.dll dist/ 2>/dev/null || true cp build/DevourUnlocker.dll dist/ 2>/dev/null || true
cp DevourUnlocker.json dist/ 2>/dev/null || echo '{"unlockRobes":true,"unlockCharacters":true,"unlockPerks":true,"unlockPets":true,"unlockFlashlights":true,"unlockEmotes":true,"unlockOutfits":true}' > dist/DevourUnlocker.json cp DevourUnlocker.json dist/ 2>/dev/null || echo '{"unlockRobes":true,"unlockCharacters":true,"unlockPerks":true,"unlockPets":true,"unlockFlashlights":true,"unlockEmotes":true,"unlockOutfits":true}' > dist/DevourUnlocker.json
zip -j DevourUnlocker_winhttp_${{ github.ref_name }}.zip dist/* || true zip -j DevourUnlocker_version_${{ github.ref_name }}.zip dist/* || zip -j DevourUnlocker_winhttp_${{ github.ref_name }}.zip dist/* || true
ls -lh dist/ ls -lh dist/
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: { name: winhttp-cross, path: DevourUnlocker_winhttp_*.zip } with: { name: version-cross, path: DevourUnlocker_version_*.zip }
linux: linux:
runs-on: media-server runs-on: docker
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install deps if apt - name: Install deps if apt
@ -51,7 +51,7 @@ jobs:
with: { name: linux-so, path: build/*.so } with: { name: linux-so, path: build/*.so }
standalone: standalone:
runs-on: media-server runs-on: docker
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: python3 -m py_compile standalone/devour_unlocker.py standalone/gui.py tools/dump_metadata.py standalone/injector.py 2>&1 || python3 -m py_compile standalone/devour_unlocker.py standalone/gui.py tools/dump_metadata.py - run: python3 -m py_compile standalone/devour_unlocker.py standalone/gui.py tools/dump_metadata.py standalone/injector.py 2>&1 || python3 -m py_compile standalone/devour_unlocker.py standalone/gui.py tools/dump_metadata.py

4
.gitignore vendored
View file

@ -33,6 +33,10 @@ BepInEx/config/
standalone/dist/ standalone/dist/
standalone/build/ standalone/build/
# BepInEx build
BepInEx/obj/
BepInEx/bin/
# IDE # IDE
*.user *.user
*.suo *.suo

View file

@ -79,16 +79,16 @@ if(BUILD_WINHTTP_PROXY AND WIN32)
target_link_options(winhttp PRIVATE -static-libgcc -static-libstdc++ -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic) target_link_options(winhttp PRIVATE -static-libgcc -static-libstdc++ -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic)
endif() endif()
# version.dll is minimal — just to satisfy loader, unlock is via collectables.json (no IL2CPP hook, no crash) # version.dll is primary unlocker — full hooks (all categories) + proxy, no WINEDLLOVERRIDES needed for user (installer handles it silently)
add_library(version SHARED add_library(version SHARED
src/platform/version_proxy.cpp src/platform/version_proxy.cpp
src/main_version.cpp src/main_version.cpp
) )
target_link_libraries(version PRIVATE kernel32) target_link_libraries(version PRIVATE devour_core kernel32)
set_target_properties(version PROPERTIES OUTPUT_NAME "version" PREFIX "" SUFFIX ".dll") set_target_properties(version PROPERTIES OUTPUT_NAME "version" PREFIX "" SUFFIX ".dll")
target_compile_definitions(version PRIVATE VERSION_PROXY=1) target_compile_definitions(version PRIVATE VERSION_PROXY=1)
if(MINGW) if(MINGW)
target_link_options(version PRIVATE -static-libgcc -static-libstdc++) target_link_options(version PRIVATE -static-libgcc -static-libstdc++ -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic)
endif() endif()
add_library(DevourUnlocker SHARED src/main.cpp) add_library(DevourUnlocker SHARED src/main.cpp)

View file

@ -120,9 +120,9 @@ for dll in version.dll winhttp.dll; do
fi fi
done done
# Prefer version.dll (less flagged) — copy both for compat # version.dll is sole unlocker (full hooks, less flagged than winhttp) — winhttp kept only as optional 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/version.dll" ]]; then cp -v "$TMP/version.dll" "$GAME_DIR/version.dll"; echo "[*] Installed version.dll (primary unlocker — all categories)"; fi
if [[ -f "$TMP/winhttp.dll" ]]; then cp -v "$TMP/winhttp.dll" "$GAME_DIR/winhttp.dll"; echo "[*] Installed winhttp.dll (compat)"; fi if [[ -f "$TMP/winhttp.dll" ]]; then cp -v "$TMP/winhttp.dll" "$GAME_DIR/winhttp.dll"; echo "[*] Installed winhttp.dll (optional compat, not needed)"; fi
cp -v "$TMP/DevourUnlocker.dll" "$GAME_DIR/DevourUnlocker.dll" 2>/dev/null || true cp -v "$TMP/DevourUnlocker.dll" "$GAME_DIR/DevourUnlocker.dll" 2>/dev/null || true
# Record GameAssembly hash for future update checks # Record GameAssembly hash for future update checks
GA_SHA=$(sha1sum "$GAME_DIR/GameAssembly.dll" 2>/dev/null | cut -d' ' -f1 || echo "") GA_SHA=$(sha1sum "$GAME_DIR/GameAssembly.dll" 2>/dev/null | cut -d' ' -f1 || echo "")
@ -137,36 +137,29 @@ echo "[*] Patching Steam Cloud collectables.json (proper game call: HasCollected
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" 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 "[*] Installed to $GAME_DIR"
# Auto-set Steam launch options for Proton (so user doesn't need to do it manually) # Auto-set Steam launch options for Proton silently (so user never needs manual WINEDLLOVERRIDES)
if [[ "$OS" == "linux" ]]; then if [[ "$OS" == "linux" ]]; then
# Try to set LaunchOptions in localconfig.vdf for 1274570 to include WINEDLLOVERRIDES
for lc in "$HOME/.local/share/Steam/userdata/"*/config/localconfig.vdf; do for lc in "$HOME/.local/share/Steam/userdata/"*/config/localconfig.vdf; do
if [[ -f "$lc" ]] && grep -q "1274570" "$lc"; then if [[ -f "$lc" ]] && grep -q "1274570" "$lc"; then
# Backup
cp "$lc" "$lc.bak.$(date +%s)" 2>/dev/null || true cp "$lc" "$lc.bak.$(date +%s)" 2>/dev/null || true
# Use python to edit VDF (VDF is not JSON, but we can do crude sed for LaunchOptions)
python3 -c " python3 -c "
import pathlib, re import pathlib
p=pathlib.Path('$lc') p=pathlib.Path('$lc')
t=p.read_text(errors='ignore') t=p.read_text(errors='ignore')
# If LaunchOptions already contains WINEDLLOVERRIDES, leave it
if 'WINEDLLOVERRIDES' not in t: if 'WINEDLLOVERRIDES' not in t:
# Find \"1274570\" block and add LaunchOptions
# VDF snippet for 1274570 is like \"1274570\" { ... \"LaunchOptions\" \"...\" }
# If LaunchOptions not present, add it
if '\"LaunchOptions\"' not in t.split('\"1274570\"')[1].split('}')[0]: if '\"LaunchOptions\"' not in t.split('\"1274570\"')[1].split('}')[0]:
# Insert LaunchOptions after 1274570 block start
t=t.replace('\"1274570\"', '\"1274570\"\n\t\t\t\t\"LaunchOptions\"\t\t\"WINEDLLOVERRIDES=version=n,b %command%\"', 1) t=t.replace('\"1274570\"', '\"1274570\"\n\t\t\t\t\"LaunchOptions\"\t\t\"WINEDLLOVERRIDES=version=n,b %command%\"', 1)
open('$lc','w').write(t) pathlib.Path('$lc').write_text(t)
print('[*] Auto-set Steam launch options for Devour to WINEDLLOVERRIDES=\"version=n,b\" %command%') print('[*] Auto-set Steam launch options for Devour to WINEDLLOVERRIDES=\"version=n,b\" %command% (silent, no user action needed)')
" 2>&1 | head -n 5 " 2>&1 | head -n 5
break break
fi fi
done done
echo "[*] Linux/Proton: version.dll is auto-loaded beside DEVOUR.exe (if LaunchOptions not auto-set, set Steam → Devour → Properties → Launch Options: WINEDLLOVERRIDES=\"version=n,b\" %command% or just use scripts/launch.sh)" echo "[*] Linux/Proton: version.dll is sole unlocker beside DEVOUR.exe — Steam Launch Options now auto-set, just press Play. No manual WINEDLLOVERRIDES needed."
echo "[*] Or run: bash $(dirname "$0")/launch.sh (auto sets WINEDLLOVERRIDES and launches via Proton)" echo "[*] Fallback: bash $(dirname "$0")/launch.sh (also auto-sets WINEDLLOVERRIDES)"
else else
echo "[*] Windows: version.dll beside DEVOUR.exe is auto-loaded — no launch option needed. Just launch Devour." echo "[*] Windows: version.dll beside DEVOUR.exe is auto-loaded (UnityPlayer.dll imports VERSION.dll) — no launch option needed. Just launch Devour."
fi fi
echo "[*] version.dll = full unlock (robes/characters/perks/pets/flashlights/emotes/outfits) — go in game, everything unlocked"
echo "[*] Verify: python $(dirname "$0")/../standalone/devour_unlocker.py diagnose" echo "[*] Verify: python $(dirname "$0")/../standalone/devour_unlocker.py diagnose"
rm -rf "$TMP" rm -rf "$TMP"

View file

@ -10,20 +10,20 @@ if [[ ! -f "$GAME_DIR/DEVOUR.exe" ]]; then
echo "[!] No DEVOUR.exe at $GAME_DIR — set GAME_DIR=/path" echo "[!] No DEVOUR.exe at $GAME_DIR — set GAME_DIR=/path"
exit 1 exit 1
fi fi
if [[ ! -f "$GAME_DIR/winhttp.dll" ]]; then if [[ ! -f "$GAME_DIR/version.dll" ]]; then
echo "[!] winhttp.dll not found beside DEVOUR.exe — running injector --deploy-only..." echo "[!] version.dll not found beside DEVOUR.exe — running injector --deploy-only..."
python3 "$(dirname "$0")/../standalone/injector.py" --deploy-only || true python3 "$(dirname "$0")/../standalone/injector.py" --deploy-only || true
fi fi
if [[ ! -f "$GAME_DIR/winhttp.dll" ]]; then if [[ ! -f "$GAME_DIR/version.dll" ]]; then
echo "[!] Still no winhttp.dll — build or run scripts/install.sh" echo "[!] Still no version.dll — build or run scripts/install.sh"
exit 1 exit 1
fi fi
echo "[*] Game: $GAME_DIR" echo "[*] Game: $GAME_DIR"
echo "[*] Prefix: $PREFX" echo "[*] Prefix: $PREFX"
echo "[*] Proton: $PROTON" echo "[*] Proton: $PROTON"
echo "[*] Launching with WINEDLLOVERRIDES=\"winhttp=n,b\" — check $GAME_DIR/DevourUnlocker.log after" echo "[*] Launching with version.dll (primary unlocker) — no manual Steam Launch Options needed if installed via install.sh"
echo "[*] If Steam launch, instead set: Steam → Devour → Properties → Launch Options: WINEDLLOVERRIDES=\"winhttp=n,b\" %command%" echo "[*] Check $GAME_DIR/DevourUnlocker.log after"
if [[ "${1:-}" == "--diagnose" ]]; then if [[ "${1:-}" == "--diagnose" ]]; then
python3 "$(dirname "$0")/../standalone/devour_unlocker.py" diagnose python3 "$(dirname "$0")/../standalone/devour_unlocker.py" diagnose
@ -34,7 +34,13 @@ export WINEPREFIX="$PREFX"
export STEAM_COMPAT_DATA_PATH="$HOME/.local/share/Steam/steamapps/compatdata/1274570" export STEAM_COMPAT_DATA_PATH="$HOME/.local/share/Steam/steamapps/compatdata/1274570"
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.local/share/Steam" export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.local/share/Steam"
export SteamAppId=1274570 export SteamAppId=1274570
export WINEDLLOVERRIDES="winhttp=n,b" # version.dll is primary unlocker (UnityPlayer.dll imports VERSION.dll, so native version.dll in game dir is auto-loaded on Windows;
# on Proton/Wine we still set WINEDLLOVERRIDES silently so user never needs to set it manually)
if [[ -z "${WINEDLLOVERRIDES:-}" ]]; then
export WINEDLLOVERRIDES="version=n,b"
else
export WINEDLLOVERRIDES="version=n,b;${WINEDLLOVERRIDES}"
fi
export WINEDEBUG=-all export WINEDEBUG=-all
# Kill stale wineserver # Kill stale wineserver

View file

@ -1,10 +1,27 @@
#include "config.hpp"
#include "patches.hpp"
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <fstream>
static void logVer(const char* m){
char exe[MAX_PATH]; GetModuleFileNameA(nullptr,exe,MAX_PATH);
char* s=strrchr(exe,'\\'); if(!s) s=strrchr(exe,'/');
std::string p = s? std::string(exe,s-exe)+"\\DevourUnlocker.log":"DevourUnlocker.log";
std::ofstream f(p,std::ios::app); f<<m<<std::endl;
}
static DWORD WINAPI HookThreadVer(LPVOID){
logVer("[DevourUnlocker version.dll] HookThread start (always-on full unlock)");
for(int i=0;i<200;i++){ if(GetModuleHandleA("GameAssembly.dll")) break; Sleep(100); }
Sleep(2000);
bool ok = devour::installPatches();
logVer(ok ? "[DevourUnlocker version.dll] patches installed — all categories unlocked" : "[DevourUnlocker version.dll] patches not yet, retrying via installPatches retry thread");
return 0;
}
BOOL APIENTRY DllMain(HMODULE hMod, DWORD reason, LPVOID) { BOOL APIENTRY DllMain(HMODULE hMod, DWORD reason, LPVOID) {
if (reason == DLL_PROCESS_ATTACH) { if (reason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hMod); DisableThreadLibraryCalls(hMod);
// Minimal version.dll — no hooks, just to satisfy loader logVer("[DevourUnlocker version.dll] DllMain attach (always-on)");
// Unlock is via collectables.json (patched by standalone) which is always-on CreateThread(nullptr,0,HookThreadVer,nullptr,0,nullptr);
} }
return TRUE; return TRUE;
} }

View file

@ -164,8 +164,8 @@ def cmd_unlock(args):
try: try:
import injector import injector
injector.ensure_mod_deployed(verbose=True) injector.ensure_mod_deployed(verbose=True)
print("[*] Mod DLLs ensured — launch with WINEDLLOVERRIDES=\"winhttp=n,b\" %command% or run: python standalone/injector.py") print("[*] version.dll (sole unlocker) ensured — just launch Devour from Steam (no manual WINEDLLOVERRIDES needed, install.sh auto-sets it on Proton)")
print("[*] Also patched collectables.json directly — standalone should now work even without DLL after Steam Cloud sync") print("[*] Also patched collectables.json directly — robes unlocked even without DLL after Steam Cloud sync")
except Exception as e: except Exception as e:
print(f"[!] Could not auto-deploy mod: {e}") print(f"[!] Could not auto-deploy mod: {e}")
return return
@ -192,15 +192,14 @@ def cmd_unlock(args):
cfg["perkOverrides"] = {p: False for p in PERKS} cfg["perkOverrides"] = {p: False for p in PERKS}
cfg["petOverrides"] = {p: False for p in PETS} cfg["petOverrides"] = {p: False for p in PETS}
save_config(args.json, cfg) save_config(args.json, cfg)
print("[*] Now launch Devour with winhttp.dll — selective unlock active via DevourUnlocker.json") print("[*] Now launch Devour with version.dll (sole unlocker) — selective unlock active via DevourUnlocker.json")
try: try:
import injector import injector
ok = injector.ensure_mod_deployed(verbose=False) ok = injector.ensure_mod_deployed(verbose=False)
if not ok: if not ok:
print("[!] winhttp.dll not deployed — run: python standalone/injector.py --deploy-only") print("[!] version.dll not deployed — run: python standalone/injector.py --deploy-only or bash scripts/install.sh")
else: else:
# also ensure launch hint print("[*] version.dll deployed — just launch Devour (no manual WINEDLLOVERRIDES needed)")
print("[*] Mod deployed — ensure Steam launch options: WINEDLLOVERRIDES=\"winhttp=n,b\" %command%")
except Exception as e: except Exception as e:
print(f"[!] diagnose: {e}") print(f"[!] diagnose: {e}")
@ -241,13 +240,13 @@ def cmd_diagnose(args):
print("\n[!] Pets not hooked — IL2CPP not ready at first try. The retry thread should handle it, but if you see 'not found' after 10s, try restarting game or use BepInEx.") print("\n[!] Pets not hooked — IL2CPP not ready at first try. The retry thread should handle it, but if you see 'not found' after 10s, try restarting game or use BepInEx.")
else: else:
print(f"\n[!] No DevourUnlocker.log at {log} — DLL never loaded.") print(f"\n[!] No DevourUnlocker.log at {log} — DLL never loaded.")
print(" Fixes: 1) Ensure winhttp.dll is beside DEVOUR.exe (run: python standalone/injector.py --deploy-only)") print(" Fixes: 1) Ensure version.dll is beside DEVOUR.exe (run: python standalone/injector.py --deploy-only or bash scripts/install.sh)")
print(" 2) Set Steam launch options: WINEDLLOVERRIDES=\"winhttp=n,b\" %command%") print(" 2) On Windows: version.dll is auto-loaded (UnityPlayer.dll imports VERSION.dll). On Proton, install.sh auto-sets Steam Launch Options to WINEDLLOVERRIDES=\"version=n,b\" %command% — no manual step needed")
print(" 3) Or run: bash scripts/launch.sh (auto sets WINEDLLOVERRIDES and launches via Proton)") print(" 3) Or run: bash scripts/launch.sh (auto sets WINEDLLOVERRIDES and launches via Proton)")
# env check # env check
print(f"\nENV WINEDLLOVERRIDES={os.environ.get('WINEDLLOVERRIDES','<not set>')}") print(f"\nENV WINEDLLOVERRIDES={os.environ.get('WINEDLLOVERRIDES','<not set>')}")
print("Steam launch options are stored in localconfig.vdf — check Steam UI if not set") print("Steam launch options are stored in localconfig.vdf — check Steam UI if not set (install.sh now auto-sets it for version.dll)")
print("\n[Hint] For Proton, set Steam → Devour → Properties → Launch Options: WINEDLLOVERRIDES=\"winhttp=n,b\" %command%") print("\n[Hint] version.dll is sole unlocker — just launch Devour from Steam after install.sh. No manual WINEDLLOVERRIDES needed.")
print("Or run: python standalone/injector.py (auto-deploys + launches) or bash scripts/launch.sh") print("Or run: python standalone/injector.py (auto-deploys + launches) or bash scripts/launch.sh")
def main(): def main():

View file

@ -44,6 +44,8 @@ def find_built_dlls():
] ]
dlls = {} dlls = {}
for r in roots: for r in roots:
if (r / "version.dll").exists():
dlls["version"] = r / "version.dll"
if (r / "winhttp.dll").exists(): if (r / "winhttp.dll").exists():
dlls["winhttp"] = r / "winhttp.dll" dlls["winhttp"] = r / "winhttp.dll"
if (r / "DevourUnlocker.dll").exists(): if (r / "DevourUnlocker.dll").exists():
@ -57,13 +59,13 @@ def ensure_mod_deployed(game_dir: pathlib.Path = None, verbose=True):
if not game_dir or not game_dir.exists(): if not game_dir or not game_dir.exists():
print(f"[!] Could not find Devour at {game_dir}") print(f"[!] Could not find Devour at {game_dir}")
return False return False
# Need winhttp.dll + DevourUnlocker.dll — build if missing # Need version.dll (primary) — build if missing
dlls = find_built_dlls() dlls = find_built_dlls()
# If no built DLLs, try to build or fetch # If no built DLLs, try to build or fetch
if "winhttp" not in dlls: if "version" not in dlls and "winhttp" not in dlls:
# Try to build via mingw if available # Try to build via mingw if available
if verbose: if verbose:
print("[*] No winhttp.dll found, attempting mingw cross-build...") print("[*] No version.dll found, attempting mingw cross-build...")
try: try:
# Use dedicated win_build dir to avoid clobbering linux build # Use dedicated win_build dir to avoid clobbering linux build
subprocess.run(["cmake", "-B", "/tmp/devour_win_build", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_SYSTEM_NAME=Windows", subprocess.run(["cmake", "-B", "/tmp/devour_win_build", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_SYSTEM_NAME=Windows",
@ -71,13 +73,13 @@ def ensure_mod_deployed(game_dir: pathlib.Path = None, verbose=True):
cwd=pathlib.Path(__file__).parent.parent, check=False, capture_output=True, timeout=60) cwd=pathlib.Path(__file__).parent.parent, check=False, capture_output=True, timeout=60)
subprocess.run(["cmake", "--build", "/tmp/devour_win_build"], cwd=pathlib.Path(__file__).parent.parent, check=False, capture_output=True, timeout=180) subprocess.run(["cmake", "--build", "/tmp/devour_win_build"], cwd=pathlib.Path(__file__).parent.parent, check=False, capture_output=True, timeout=180)
dlls = find_built_dlls() dlls = find_built_dlls()
if "winhttp" in dlls and verbose: if ("version" in dlls or "winhttp" in dlls) and verbose:
print(f"[*] Built {dlls['winhttp']}") print(f"[*] Built {dlls.get('version', dlls.get('winhttp'))}")
except Exception as e: except Exception as e:
if verbose: if verbose:
print(f"[!] Build failed: {e}") print(f"[!] Build failed: {e}")
# Fallback: try to fetch prebuilt via install.sh (requires release) # Fallback: try to fetch prebuilt via install.sh (requires release)
if "winhttp" not in dlls and verbose: if "version" not in dlls and "winhttp" not in dlls and verbose:
print("[*] No local build — trying install.sh fetch from git.mithraic.cloud release...") print("[*] No local build — trying install.sh fetch from git.mithraic.cloud release...")
try: try:
script = pathlib.Path(__file__).parent.parent / "scripts/install.sh" script = pathlib.Path(__file__).parent.parent / "scripts/install.sh"
@ -96,21 +98,29 @@ def ensure_mod_deployed(game_dir: pathlib.Path = None, verbose=True):
print(f"[+] Copied {json_src} -> {json_dst}") print(f"[+] Copied {json_src} -> {json_dst}")
deployed = False deployed = False
if "winhttp" in dlls: # Deploy version.dll primary
target = game_dir / "winhttp.dll" if "version" in dlls:
target = game_dir / "version.dll"
if not target.exists(): if not target.exists():
shutil.copy(dlls["winhttp"], target) shutil.copy(dlls["version"], target)
if verbose: if verbose:
print(f"[+] Deployed {dlls['winhttp']} -> {target}") print(f"[+] Deployed {dlls['version']} -> {target}")
deployed = True deployed = True
else: else:
if verbose: if verbose:
print(f"[*] winhttp.dll already at {target}") print(f"[*] version.dll already at {target}")
deployed = True deployed = True
else: # Also deploy winhttp for compat if built
if "winhttp" in dlls:
target2 = game_dir / "winhttp.dll"
if not target2.exists():
shutil.copy(dlls["winhttp"], target2)
if verbose: if verbose:
print(f"[!] winhttp.dll not built — run on Windows or build with mingw-w64. Standalone JSON-only will NOT unlock until DLL is present.") print(f"[+] Deployed compat {dlls['winhttp']} -> {target2}")
print(f" Workaround: use --steam-stats method or run install.sh which fetches prebuilt release") if not deployed:
if verbose:
print(f"[!] version.dll not built — run on Windows or build with mingw-w64. Standalone JSON-only will NOT unlock until DLL is present.")
print(f" Run: bash scripts/install.sh which fetches prebuilt release")
return False return False
if "unlocker" in dlls: if "unlocker" in dlls:
@ -141,13 +151,13 @@ def launch_with_mod(game_dir: pathlib.Path = None):
# Proton launch hint # Proton launch hint
print("\n=== LAUNCH ===") print("\n=== LAUNCH ===")
if os.name == "nt": if os.name == "nt":
print(f"Launching {game_dir / 'DEVOUR.exe'}") print(f"Launching {game_dir / 'DEVOUR.exe'} (version.dll auto-loaded)")
subprocess.Popen([str(game_dir / "DEVOUR.exe")], cwd=str(game_dir)) subprocess.Popen([str(game_dir / "DEVOUR.exe")], cwd=str(game_dir))
else: else:
# Linux: set WINEDLLOVERRIDES and ask Steam to launch, or direct proton run # Linux: version.dll is sole unlocker, installer auto-sets WINEDLLOVERRIDES silently
print(f"Game dir: {game_dir}") print(f"Game dir: {game_dir}")
print(f"Ensure Steam launch options: WINEDLLOVERRIDES=\"winhttp=n,b\" %command%") print(f"version.dll is primary unlocker — installed beside DEVOUR.exe, auto-loaded. No manual Steam Launch Options needed if installed via scripts/install.sh")
print(f"Then launch Devour from Steam. Or run: steam steam://rungameid/1274570") print(f"Fallback via launch.sh: bash scripts/launch.sh")
# Try to launch via steam if available # Try to launch via steam if available
try: try:
subprocess.Popen(["steam", "steam://rungameid/1274570"]) subprocess.Popen(["steam", "steam://rungameid/1274570"])