#!/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))