Restructure into multi-game UltraWidePatches umbrella

Generalize the project from a single GB4 fix into a collection that can host
ultrawide patches for many games.

- games/<Game>/{SUWSF.ini,game.conf,README.md}; GB4 moved in as the first entry
- game.conf carries all game-specific data (appid, exe, paths, loader, launch
  option), so install.sh is fully generic: ./install.sh <Game> [uninstall]
- installer gains GAME_DIR / PREFIX_INI_FILE / LOADER_NAME overrides; verified
  install -> idempotent re-run -> uninstall leaves the tree pristine
- tools de-hardcoded: dump_decrypted.py takes an exe name, read_protonlog.sh
  takes an appid, aob.py takes a dump + game ini, build_release.sh takes a game
- README rewritten as an umbrella index with an "adding a game" guide
- LICENSE copyright -> programmingPug

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 11:21:20 -04:00
co-authored by Claude Opus 4.8
parent b37677cbc1
commit 5d6fd277c9
14 changed files with 294 additions and 166 deletions
+9 -8
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Dump the DECRYPTED GB4-Win64-Shipping.exe image from a running (Proton) process.
"""Dump a DRM-DECRYPTED game image from a running (Proton) process.
Why: the on-disk exe is SteamStub-encrypted (.text entropy 8.0). The real UE4
code only exists in memory after the stub decrypts it at launch. This grabs that
@@ -8,7 +8,7 @@ memory so we can verify/derive the exact SUWSF byte patterns offline.
Usage:
# 1. Launch Gundam Breaker 4 (get to the main menu / lobby).
# 2. Run:
python3 tools/dump_decrypted.py
python3 tools/dump_decrypted.py <ExeName.exe> [pid]
# -> writes scratch dump + region metadata, then prints AOB match report.
If you get "Operation not permitted", either run with sudo, or temporarily:
@@ -16,8 +16,8 @@ If you get "Operation not permitted", either run with sudo, or temporarily:
"""
import ctypes, ctypes.util, os, re, sys, json, struct, glob
MODULE_HINT = "GB4-Win64-Shipping.exe"
OUTDIR = os.environ.get("GB4_DUMP_DIR", "/tmp/claude-1000/-home-ckoch-Documents-Development-BG4-UltraWide-fix/1a8ebba4-6fbe-45ae-a873-0e69d867e0db/scratchpad/gb4dump")
MODULE_HINT = next((a for a in sys.argv[1:] if a.lower().endswith(".exe")), "GB4-Win64-Shipping.exe")
OUTDIR = os.environ.get("UWP_DUMP_DIR", os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "dumps"))
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
@@ -109,18 +109,19 @@ def main():
meta["regions"].append({"start": start, "end": end, "rva": rva,
"perms": perms, "size": size, "got": len(data)})
print(f" dumped 0x{start:x}-0x{end:x} {perms} rva=0x{rva:x} ({len(data)} bytes)")
dump_path = os.path.join(OUTDIR, "gb4_decrypted.bin")
stem = os.path.splitext(os.path.basename(MODULE_HINT))[0]
dump_path = os.path.join(OUTDIR, f"{stem}_decrypted.bin")
with open(dump_path, "wb") as f:
f.write(blob)
with open(os.path.join(OUTDIR, "gb4_dump_meta.json"), "w") as f:
with open(os.path.join(OUTDIR, f"{stem}_meta.json"), "w") as f:
json.dump(meta, f, indent=2)
print(f"[+] wrote {dump_path} ({len(blob)} bytes, rva-aligned)")
print(f"[+] wrote {os.path.join(OUTDIR, 'gb4_dump_meta.json')}")
print(f"[+] wrote {os.path.join(OUTDIR, stem + '_meta.json')}")
# quick sanity: is it decrypted? look for the UE4 version string / a UTF-16 hint
if b"++UE4+Release-4.27" in blob or b"CalculateProjectionMatrix" in blob:
print("[+] decrypted UE4 code confirmed in dump.")
print("\nNext: python3 tools/aob.py (verifies SUWSF patterns against this dump)")
print(f"\nNext: python3 tools/aob.py {dump_path} games/<Game>/SUWSF.ini")
if __name__ == "__main__":