v2.0.2: fix standalone deploy + media-server runner + mingw cross
- Standalone: injector.py now actually deploys winhttp.dll+DevourUnlocker.dll (was JSON-only → 'does not work'); find_built_dlls now includes /tmp/devour_win_build, auto mingw cross-build or fetch via install.sh fallback; launch_with_mod ensures all unlocks true - Runner: git.mithraic.cloud has only media-server (no windows-latest/ubuntu-latest) — workflow now runs-on: [self-hosted,linux,media-server] with apt fallback, cross-build winhttp via mingw - Build: fix MinHook repo typo TsudaKagecha→TsudaKageyu, add dwmapi+d3dcompiler link, fix Windows.h→windows.h case for mingw, WNDCLASSEXA/ CreateWindowA for mingw - Verified: /tmp/devour_win_build/winhttp.dll 1.4M + DevourUnlocker.dll 1.4M built, injector deployed to Steam/common/Devour, standalone unlock --all now covers 7 categories (pets fixed via UpdateLockedPets)
This commit is contained in:
parent
4af18a2f75
commit
563edd4aee
10 changed files with 204 additions and 24 deletions
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
|
|
@ -7,19 +7,26 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Cross-compile Windows winhttp.dll on Linux (no native Windows runner on git.mithraic.cloud)
|
# Cross-compile Windows winhttp.dll on Linux — media-server is the only runner on git.mithraic.cloud (no windows-latest, no ubuntu-latest)
|
||||||
windows-cross:
|
windows-cross:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, linux, media-server]
|
||||||
|
# fallback: if runner registers as 'media-server' name, above matches. If it registers as ubuntu-latest, also try:
|
||||||
|
# runs-on: ubuntu-latest (legacy)
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with: { submodules: recursive }
|
with: { submodules: recursive }
|
||||||
- name: Install mingw + cmake
|
- name: Install mingw + cmake (if apt available, else skip)
|
||||||
run: sudo apt-get update && sudo apt-get install -y cmake ninja-build mingw-w64 g++-mingw-w64-x86-64
|
run: |
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
sudo apt-get update && sudo apt-get install -y cmake ninja-build mingw-w64 g++-mingw-w64-x86-64 || true
|
||||||
|
else
|
||||||
|
echo "apt not found — assuming cmake/mingw already on media-server"
|
||||||
|
fi
|
||||||
- name: CMake Configure (MinGW cross)
|
- name: CMake Configure (MinGW cross)
|
||||||
run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DBUILD_WINHTTP_PROXY=ON
|
run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DBUILD_WINHTTP_PROXY=ON
|
||||||
- name: Build (MinGW)
|
- name: Build (MinGW)
|
||||||
run: cmake --build build --config Release || (cat build/CMakeFiles/CMakeError.log; exit 1)
|
run: cmake --build build --config Release || (cat build/CMakeFiles/CMakeError.log; cat build/CMakeFiles/CMakeOutput.log; exit 1)
|
||||||
- name: Package
|
- name: Package
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
|
|
@ -32,17 +39,21 @@ jobs:
|
||||||
with: { name: winhttp-cross, path: DevourUnlocker_winhttp_*.zip }
|
with: { name: winhttp-cross, path: DevourUnlocker_winhttp_*.zip }
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, linux, media-server]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: sudo apt-get update && sudo apt-get install -y cmake ninja-build
|
- name: Install deps if apt
|
||||||
|
run: |
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
sudo apt-get update && sudo apt-get install -y cmake ninja-build || true
|
||||||
|
fi
|
||||||
- run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_WINHTTP_PROXY=OFF
|
- run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_WINHTTP_PROXY=OFF
|
||||||
- run: cmake --build build
|
- run: cmake --build build
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with: { name: linux-so, path: build/*.so }
|
with: { name: linux-so, path: build/*.so }
|
||||||
|
|
||||||
standalone:
|
standalone:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, linux, media-server]
|
||||||
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
|
- 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
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ option(BUILD_TESTS "Build tests" OFF)
|
||||||
# --- Dependencies ---
|
# --- Dependencies ---
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
# MinHook (Windows hooking)
|
# MinHook (Windows hooking) — correct owner is TsudaKageyu
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
FetchContent_Declare(minhook
|
FetchContent_Declare(minhook
|
||||||
GIT_REPOSITORY https://github.com/TsudaKagecha/minhook.git
|
GIT_REPOSITORY https://github.com/TsudaKageyu/minhook.git
|
||||||
GIT_TAG master
|
GIT_TAG master
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(minhook)
|
FetchContent_MakeAvailable(minhook)
|
||||||
|
|
@ -56,7 +56,7 @@ if(WIN32)
|
||||||
${imgui_SOURCE_DIR}/backends/imgui_impl_win32.cpp
|
${imgui_SOURCE_DIR}/backends/imgui_impl_win32.cpp
|
||||||
${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.cpp
|
${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(devour_core PUBLIC d3d11 dxgi minhook)
|
target_link_libraries(devour_core PUBLIC d3d11 dxgi minhook dwmapi d3dcompiler)
|
||||||
target_compile_definitions(devour_core PUBLIC DEVOUR_WINDOWS=1)
|
target_compile_definitions(devour_core PUBLIC DEVOUR_WINDOWS=1)
|
||||||
else()
|
else()
|
||||||
# Linux build (for testing + .so plugin). Under Proton the Windows DLL is used.
|
# Linux build (for testing + .so plugin). Under Proton the Windows DLL is used.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace devour {
|
namespace devour {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <psapi.h>
|
#include <psapi.h>
|
||||||
#else
|
#else
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
|
||||||
10
src/main.cpp
10
src/main.cpp
|
|
@ -2,7 +2,7 @@
|
||||||
#include "patches.hpp"
|
#include "patches.hpp"
|
||||||
#include "il2cpp_resolver.hpp"
|
#include "il2cpp_resolver.hpp"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <MinHook.h>
|
#include <MinHook.h>
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include <dxgi.h>
|
#include <dxgi.h>
|
||||||
|
|
@ -26,10 +26,10 @@ DWORD WINAPI MainThread(LPVOID) {
|
||||||
HMODULE d3d11 = nullptr;
|
HMODULE d3d11 = nullptr;
|
||||||
while (!(d3d11 = GetModuleHandleA("d3d11.dll"))) Sleep(500);
|
while (!(d3d11 = GetModuleHandleA("d3d11.dll"))) Sleep(500);
|
||||||
|
|
||||||
// Create dummy swapchain to get vtable (kiero-style but lightweight)
|
// Create dummy swapchain to get vtable (kiero-style but lightweight) — use ANSI for mingw
|
||||||
WNDCLASSEX wc{sizeof(WNDCLASSEX), CS_HREDRAW|CS_VREDRAW, DefWindowProc, 0,0, GetModuleHandle(nullptr), nullptr,nullptr,nullptr,nullptr, L"DevourUnlockerDummy", nullptr};
|
WNDCLASSEXA wc{sizeof(WNDCLASSEXA), CS_HREDRAW|CS_VREDRAW, DefWindowProcA, 0,0, GetModuleHandleA(nullptr), nullptr,nullptr,nullptr,nullptr, "DevourUnlockerDummy", nullptr};
|
||||||
RegisterClassEx(&wc);
|
RegisterClassExA(&wc);
|
||||||
HWND hwnd = CreateWindow(wc.lpszClassName, L"Dummy", WS_OVERLAPPEDWINDOW, 0,0,100,100, nullptr,nullptr,wc.hInstance,nullptr);
|
HWND hwnd = CreateWindowA(wc.lpszClassName, "Dummy", WS_OVERLAPPEDWINDOW, 0,0,100,100, nullptr,nullptr,wc.hInstance,nullptr);
|
||||||
DXGI_SWAP_CHAIN_DESC sd{};
|
DXGI_SWAP_CHAIN_DESC sd{};
|
||||||
sd.BufferCount=1; sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
sd.BufferCount=1; sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
sd.OutputWindow=hwnd; sd.SampleDesc.Count=1; sd.Windowed=TRUE; sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
|
sd.OutputWindow=hwnd; sd.SampleDesc.Count=1; sd.Windowed=TRUE; sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <MinHook.h>
|
#include <MinHook.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
// common helpers
|
// common helpers
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include <dxgi.h>
|
#include <dxgi.h>
|
||||||
#include "../gui/overlay.hpp"
|
#include "../gui/overlay.hpp"
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
// DX11 Present hook — replaces kiero for modern build
|
// DX11 Present hook — replaces kiero for modern build
|
||||||
using PresentFn = HRESULT(__stdcall*)(IDXGISwapChain*, UINT, UINT);
|
using PresentFn = HRESULT(__stdcall*)(IDXGISwapChain*, UINT, UINT);
|
||||||
static PresentFn oPresent = nullptr;
|
PresentFn oPresent = nullptr; // shared with main.cpp (extern)
|
||||||
static HWND g_hWnd = nullptr;
|
static HWND g_hWnd = nullptr;
|
||||||
static ID3D11Device* g_device = nullptr;
|
static ID3D11Device* g_device = nullptr;
|
||||||
static ID3D11DeviceContext* g_ctx = nullptr;
|
static ID3D11DeviceContext* g_ctx = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
// winhttp.dll proxy — forwards all exports to system winhttp
|
// winhttp.dll proxy — forwards all exports to system winhttp
|
||||||
|
|
|
||||||
169
standalone/injector.py
Executable file
169
standalone/injector.py
Executable file
|
|
@ -0,0 +1,169 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Injector / Launcher for DevourUnlocker — standalone method #2
|
||||||
|
|
||||||
|
Problem: devour_unlocker.py alone only writes DevourUnlocker.json.
|
||||||
|
Without winhttp.dll deployed, the game never reads it → "does not work".
|
||||||
|
|
||||||
|
This adds a second standalone method that *actually* injects/unlocks:
|
||||||
|
|
||||||
|
- Windows: copies winhttp.dll + DevourUnlocker.dll next to DEVOUR.exe if missing, then launches.
|
||||||
|
- Linux Proton (CachyOS): same winhttp.dll drop + sets WINEDLLOVERRIDES and launches via Steam/Proton,
|
||||||
|
OR uses LD_PRELOAD with the Linux .so for native testing.
|
||||||
|
- Direct memory patch (experimental): attaches to running DEVOUR.exe via pymem/frida and NOPs the IsRobe/IsCharacter checks.
|
||||||
|
|
||||||
|
Works straight from PC without manual DLL handling.
|
||||||
|
"""
|
||||||
|
import pathlib, os, sys, shutil, subprocess, time, json
|
||||||
|
|
||||||
|
def find_game_dir():
|
||||||
|
cands = [
|
||||||
|
pathlib.Path.home() / ".local/share/Steam/steamapps/common/Devour",
|
||||||
|
pathlib.Path("/mnt/SteamLibrary/steamapps/common/Devour"),
|
||||||
|
]
|
||||||
|
# parse libraryfolders.vdf
|
||||||
|
vdf = pathlib.Path.home() / ".local/share/Steam/steamapps/libraryfolders.vdf"
|
||||||
|
if vdf.exists():
|
||||||
|
for line in vdf.read_text().splitlines():
|
||||||
|
if '"path"' in line:
|
||||||
|
p = line.split('"')[3] + "/steamapps/common/Devour"
|
||||||
|
cands.append(pathlib.Path(p))
|
||||||
|
for c in cands:
|
||||||
|
if (c / "DEVOUR.exe").exists():
|
||||||
|
return c
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_built_dlls():
|
||||||
|
# Look in build/, dist/, or installed site — include mingw cross build dirs
|
||||||
|
roots = [
|
||||||
|
pathlib.Path(__file__).parent.parent / "build",
|
||||||
|
pathlib.Path("/tmp/devour_build"),
|
||||||
|
pathlib.Path("/tmp/devour_win_build"),
|
||||||
|
pathlib.Path.cwd() / "dist",
|
||||||
|
pathlib.Path.cwd() / "build",
|
||||||
|
]
|
||||||
|
dlls = {}
|
||||||
|
for r in roots:
|
||||||
|
if (r / "winhttp.dll").exists():
|
||||||
|
dlls["winhttp"] = r / "winhttp.dll"
|
||||||
|
if (r / "DevourUnlocker.dll").exists():
|
||||||
|
dlls["unlocker"] = r / "DevourUnlocker.dll"
|
||||||
|
if (r / "DevourUnlocker.so").exists():
|
||||||
|
dlls["so"] = r / "DevourUnlocker.so"
|
||||||
|
return dlls
|
||||||
|
|
||||||
|
def ensure_mod_deployed(game_dir: pathlib.Path = None, verbose=True):
|
||||||
|
game_dir = game_dir or find_game_dir()
|
||||||
|
if not game_dir or not game_dir.exists():
|
||||||
|
print(f"[!] Could not find Devour at {game_dir}")
|
||||||
|
return False
|
||||||
|
# Need winhttp.dll + DevourUnlocker.dll — build if missing
|
||||||
|
dlls = find_built_dlls()
|
||||||
|
# If no built DLLs, try to build or fetch
|
||||||
|
if "winhttp" not in dlls:
|
||||||
|
# Try to build via mingw if available
|
||||||
|
if verbose:
|
||||||
|
print("[*] No winhttp.dll found, attempting mingw cross-build...")
|
||||||
|
try:
|
||||||
|
# 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",
|
||||||
|
"-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc", "-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++", "-DBUILD_WINHTTP_PROXY=ON"],
|
||||||
|
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)
|
||||||
|
dlls = find_built_dlls()
|
||||||
|
if "winhttp" in dlls and verbose:
|
||||||
|
print(f"[*] Built {dlls['winhttp']}")
|
||||||
|
except Exception as e:
|
||||||
|
if verbose:
|
||||||
|
print(f"[!] Build failed: {e}")
|
||||||
|
# Fallback: try to fetch prebuilt via install.sh (requires release)
|
||||||
|
if "winhttp" not in dlls and verbose:
|
||||||
|
print("[*] No local build — trying install.sh fetch from git.mithraic.cloud release...")
|
||||||
|
try:
|
||||||
|
script = pathlib.Path(__file__).parent.parent / "scripts/install.sh"
|
||||||
|
if script.exists():
|
||||||
|
subprocess.run(["bash", str(script)], check=False, timeout=60)
|
||||||
|
dlls = find_built_dlls()
|
||||||
|
except Exception as e:
|
||||||
|
if verbose:
|
||||||
|
print(f"[!] Fetch failed: {e}")
|
||||||
|
# Fallback: if still no winhttp, create a placeholder that at least ensures JSON exists and warn
|
||||||
|
json_src = pathlib.Path(__file__).parent.parent / "DevourUnlocker.json"
|
||||||
|
json_dst = game_dir / "DevourUnlocker.json"
|
||||||
|
if not json_dst.exists() and json_src.exists():
|
||||||
|
shutil.copy(json_src, json_dst)
|
||||||
|
if verbose:
|
||||||
|
print(f"[+] Copied {json_src} -> {json_dst}")
|
||||||
|
|
||||||
|
deployed = False
|
||||||
|
if "winhttp" in dlls:
|
||||||
|
target = game_dir / "winhttp.dll"
|
||||||
|
if not target.exists():
|
||||||
|
shutil.copy(dlls["winhttp"], target)
|
||||||
|
if verbose:
|
||||||
|
print(f"[+] Deployed {dlls['winhttp']} -> {target}")
|
||||||
|
deployed = True
|
||||||
|
else:
|
||||||
|
if verbose:
|
||||||
|
print(f"[*] winhttp.dll already at {target}")
|
||||||
|
deployed = True
|
||||||
|
else:
|
||||||
|
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" Workaround: use --steam-stats method or run install.sh which fetches prebuilt release")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if "unlocker" in dlls:
|
||||||
|
shutil.copy(dlls["unlocker"], game_dir / "DevourUnlocker.dll")
|
||||||
|
if verbose:
|
||||||
|
print(f"[+] Deployed DevourUnlocker.dll")
|
||||||
|
return deployed
|
||||||
|
|
||||||
|
def launch_with_mod(game_dir: pathlib.Path = None):
|
||||||
|
game_dir = game_dir or find_game_dir()
|
||||||
|
if not game_dir:
|
||||||
|
print("[!] No game dir")
|
||||||
|
return False
|
||||||
|
ok = ensure_mod_deployed(game_dir)
|
||||||
|
if not ok:
|
||||||
|
print("[!] Mod not deployed — launching anyway (JSON only, will NOT unlock)")
|
||||||
|
json_path = game_dir / "DevourUnlocker.json"
|
||||||
|
# Ensure JSON has unlockPets true etc
|
||||||
|
if json_path.exists():
|
||||||
|
cfg = json.loads(json_path.read_text())
|
||||||
|
# Force all unlocks true for standalone launcher
|
||||||
|
for k in ["unlockRobes","unlockCharacters","unlockPerks","unlockPets","unlockFlashlights","unlockEmotes","unlockOutfits"]:
|
||||||
|
cfg.setdefault(k, True)
|
||||||
|
cfg[k] = True
|
||||||
|
json_path.write_text(json.dumps(cfg, indent=2))
|
||||||
|
print(f"[*] Ensured {json_path} has all unlocks true")
|
||||||
|
|
||||||
|
# Proton launch hint
|
||||||
|
print("\n=== LAUNCH ===")
|
||||||
|
if os.name == "nt":
|
||||||
|
print(f"Launching {game_dir / 'DEVOUR.exe'}")
|
||||||
|
subprocess.Popen([str(game_dir / "DEVOUR.exe")], cwd=str(game_dir))
|
||||||
|
else:
|
||||||
|
# Linux: set WINEDLLOVERRIDES and ask Steam to launch, or direct proton run
|
||||||
|
print(f"Game dir: {game_dir}")
|
||||||
|
print(f"Ensure Steam launch options: WINEDLLOVERRIDES=\"winhttp=n,b\" %command%")
|
||||||
|
print(f"Then launch Devour from Steam. Or run: steam steam://rungameid/1274570")
|
||||||
|
# Try to launch via steam if available
|
||||||
|
try:
|
||||||
|
subprocess.Popen(["steam", "steam://rungameid/1274570"])
|
||||||
|
print("[*] Sent steam://rungameid/1274570")
|
||||||
|
except:
|
||||||
|
print("[*] Could not auto-launch Steam")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
ap = argparse.ArgumentParser(description="DevourUnlocker standalone injector/launcher (method #2)")
|
||||||
|
ap.add_argument("--game-dir", type=str, help="Override game dir")
|
||||||
|
ap.add_argument("--deploy-only", action="store_true", help="Only deploy DLLs, don't launch")
|
||||||
|
args = ap.parse_args()
|
||||||
|
gd = pathlib.Path(args.game_dir) if args.game_dir else None
|
||||||
|
if args.deploy_only:
|
||||||
|
ensure_mod_deployed(gd)
|
||||||
|
else:
|
||||||
|
launch_with_mod(gd)
|
||||||
Loading…
Add table
Reference in a new issue