From 59ee13de9d092233164aea2133e82bcf3d211f67 Mon Sep 17 00:00:00 2001 From: ckoch Date: Tue, 21 Jul 2026 16:33:56 -0400 Subject: [PATCH] Burglin' Gnomes: finalize fix, docs, and BepInEx install support Final tuned values baked in as defaults: HudScale=0.8, WorldLabelScale=0.8. - install.sh gains METHOD=bepinex: installs BepInEx from vendor + the prebuilt plugin, with a matching uninstall that leaves BepInEx/ alone (other mods may depend on it). Success banner is now method-aware. - Ship the prebuilt plugin DLL so users don't need a .NET SDK. - Per-game README documenting why the camera is deliberately untouched (orthographic, constant orthographicSize = already Hor+), the two-canvas UI problem, tuning, and where the plugin log actually lives. - Root README lists both games and both fix methods. Co-Authored-By: Claude Opus 4.8 --- README.md | 22 ++-- .../BurglinGnomes.UltraWideFix.dll | Bin 0 -> 10752 bytes games/BurglinGnomes/README.md | 98 ++++++++++++++++++ games/BurglinGnomes/game.conf | 3 +- games/BurglinGnomes/plugin/UltraWideFix.cs | 4 +- install.sh | 58 ++++++++--- 6 files changed, 162 insertions(+), 23 deletions(-) create mode 100644 games/BurglinGnomes/BurglinGnomes.UltraWideFix.dll create mode 100644 games/BurglinGnomes/README.md diff --git a/README.md b/README.md index 93d9a1a..d5a9571 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,19 @@ Ultrawide (**32:9 / 21:9**) fixes for PC games that ship with poor or no ultrawide support — built and tested on a **Samsung 49" Odyssey G93SC (5120×1440)**, and **Proton/Linux-first**. -Most of these force **Hor+ FOV** (a wider screen shows you *more*, instead of zooming the camera in) and disable pillarboxing, by patching the game in memory at runtime with [SUWSF](https://github.com/PhantomGamers/SUWSF) + [Ultimate ASI Loader](https://github.com/ThirteenAG/Ultimate-ASI-Loader). +Fixes use whichever approach the engine calls for: + +- **SUWSF + Ultimate ASI Loader** — runtime memory patching, for Unreal titles (and anything DRM-wrapped, where static patching is impossible). +- **BepInEx plugin** — a proper C# mod, for Unity titles with the Mono backend. + +Most force **Hor+ FOV** (a wider screen shows you *more* rather than zooming in) and fix UI that assumes 16:9. Some games already render correctly and only need UI work — [Burglin' Gnomes](games/BurglinGnomes/) is one, and the fix deliberately leaves its camera alone. ## Supported games -| Game | Engine | Status | -|---|---|---| -| [Gundam Breaker 4](games/GundamBreaker4/) | Unreal Engine 4.27 | ✅ Confirmed working in gameplay at 5120×1440 | +| Game | Engine | Method | Status | +|---|---|---|---| +| [Gundam Breaker 4](games/GundamBreaker4/) | Unreal Engine 4.27 | SUWSF | ✅ Confirmed working at 5120×1440 | +| [Burglin' Gnomes](games/BurglinGnomes/) | Unity 6 (Mono) | BepInEx plugin | ✅ Confirmed working at 5120×1440 | ## Install @@ -32,7 +38,8 @@ Config-file tweaks (e.g. UE4's `AspectRatioAxisConstraint=MaintainYFOV`) typical | Path | What | |---|---| -| `games//SUWSF.ini` | That game's patch definitions | +| `games//SUWSF.ini` | Patch definitions (SUWSF games) | +| `games//plugin/` | C# plugin source + prebuilt DLL (BepInEx games) | | `games//game.conf` | Game metadata (appid, exe, paths, launch option) | | `install.sh` | Generic installer / uninstaller | | `tools/build_release.sh` | Builds a per-game drop-in release zip | @@ -45,8 +52,9 @@ Config-file tweaks (e.g. UE4's `AspectRatioAxisConstraint=MaintainYFOV`) typical ## Adding a game 1. `mkdir games/` with a `game.conf` (copy GB4's as a template) and a `SUWSF.ini`. -2. Start from the known UE4 patterns in [`games/GundamBreaker4/SUWSF.ini`](games/GundamBreaker4/SUWSF.ini) — they match across many UE4 titles. -3. If a pattern misses, use `tools/dump_decrypted.py` + `tools/aob.py` to derive the game's exact bytes. +2. Set `METHOD=` in `game.conf` to `suwsf` or `bepinex`. +3. **Unreal:** start from the UE4 patterns in [`games/GundamBreaker4/SUWSF.ini`](games/GundamBreaker4/SUWSF.ini) — they match across many UE4 titles. If a pattern misses, use `tools/dump_decrypted.py` + `tools/aob.py` to derive the exact bytes. +4. **Unity:** install BepInEx + UnityExplorer and inspect live with [`tools/unity_diag.cs`](tools/unity_diag.cs) / [`tools/unity_uitune.cs`](tools/unity_uitune.cs), then write a plugin (copy `games/BurglinGnomes/plugin/`). ## License diff --git a/games/BurglinGnomes/BurglinGnomes.UltraWideFix.dll b/games/BurglinGnomes/BurglinGnomes.UltraWideFix.dll new file mode 100644 index 0000000000000000000000000000000000000000..e0648e87dbec5b77913a9b8fcd1fb4dfe3f6accf GIT binary patch literal 10752 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 literal 0 HcmV?d00001 diff --git a/games/BurglinGnomes/README.md b/games/BurglinGnomes/README.md new file mode 100644 index 0000000..a2948a9 --- /dev/null +++ b/games/BurglinGnomes/README.md @@ -0,0 +1,98 @@ +# Burglin' Gnomes — UltraWide Fix + +✅ **Confirmed working at 5120×1440 (32:9) on Proton.** + +- **Engine:** Unity 6 (6000.0.62f1), Mono backend, URP + Cinemachine +- **Steam AppID:** 3844970 +- **Executable:** `Gnomium.exe` +- **Method:** BepInEx plugin (no DRM, no memory patching needed) + +## What it fixes — and what it deliberately doesn't + +**The camera is already correct.** It's *orthographic* with a constant `orthographicSize` of 5.0, so a wider display genuinely shows more world (visible width 17.8 → 35.6 going 16:9 → 32:9). That's textbook Hor+, and this fix does not touch it. `fieldOfView` reads 60 at every resolution simply because orthographic cameras don't use it — a red herring if you go looking. + +**The problem is entirely UI**, in two separate places needing two different levers: + +| Canvas | Problem | Lever | +|---|---|---| +| HUD (`ScreenSpaceOverlay` + `ScaleWithScreenSize`) | Ships `matchWidthOrHeight = 0.453` (width-biased). At 5120px that yields scaleFactor **1.948** — a ballooned HUD with overlapping bottom/centre text. | `match = 1` (scale by height) + raise `referenceResolution.y`, shrinking the HUD *and* giving the layout more logical room | +| In-world labels (`WorldSpace`, e.g. `NamePlate`) | At 32:9 you see twice as much world, so far more labels are on screen and can collide | Canvas `transform.localScale` | + +> **Gotcha:** `CanvasScaler.scaleFactor` does nothing on a WorldSpace canvas — `CanvasScaler.HandleWorldCanvas()` overwrites it with `dynamicPixelsPerUnit` every frame. Scaling the transform is the only thing that works. + +## Install (Linux / Proton) + +```bash +./install.sh BurglinGnomes +``` + +Then **required** — Steam → Properties → Launch Options: + +``` +WINEDLLOVERRIDES="winhttp=n,b" %command% +``` + +Without it Proton ignores BepInEx's `winhttp.dll` loader and the plugin never runs. + +## Install (Windows) + +Extract the release zip into the game folder (next to `Gnomium.exe`). No launch option needed. + +## Tuning + +Config is generated on first launch: + +``` +/BepInEx/config/com.programmingpug.burglingnomes.ultrawidefix.cfg +``` + +**Edits apply live (~2s) — no restart.** BepInEx doesn't reload config on its own, so the plugin watches the file's mtime and calls `Config.Reload()` itself. + +| Setting | Default | Notes | +|---|---|---| +| `HudScale` | `0.8` | HUD size. Lower = smaller HUD *and* more layout room (cures overlapping text) | +| `WorldLabelScale` | `0.8` | In-world nameplate size | +| `MatchWidthOrHeight` | `1.0` | **Leave at 1.** Lower re-introduces width scaling and cramps the layout vertically | +| `DesignHeight` | `1080` | The game's design height; don't change | +| `Verbose` | `false` | Log every canvas adjustment | + +`HudScale` → resulting HUD scale at 5120×1440 (stock is 1.333): + +| HudScale | 1.0 | 0.85 | **0.8** | 0.6 | +|---|---|---|---|---| +| scale | 1.333 | 1.133 | **1.067** | 0.800 | + +## Verifying it works + +``` +/BepInEx/LogOutput.log +``` + +That's the plugin's log — **not** UnityExplorer's in-game Log window, which only shows Unity `Debug.Log`. You should see: + +``` +Burglin' Gnomes UltraWide Fix 1.0.0 loaded. +HUD 'Canvas' ref (1920.00, 1080.00) -> (1920.00, 1350.00) match=1 => scale=1.067 +World 'NamePlate' localScale (1.00, 1.00, 1.00) -> (0.80, 0.80, 0.80) +``` + +If a line is absent, that adjustment didn't happen. + +## Uninstall + +```bash +./install.sh BurglinGnomes uninstall +``` + +Removes the loader and plugin. `BepInEx/` is left in place in case other mods use it. + +## Building the plugin from source + +Prebuilt `BurglinGnomes.UltraWideFix.dll` ships here, so this is only needed if you change it: + +```bash +cd plugin +dotnet build -c Release -p:GameDir="/path/to/Burglin' Gnomes" +``` + +Requires the .NET SDK; it references the game's own assemblies and redistributes none of them. diff --git a/games/BurglinGnomes/game.conf b/games/BurglinGnomes/game.conf index 22db7b2..f7df1e2 100644 --- a/games/BurglinGnomes/game.conf +++ b/games/BurglinGnomes/game.conf @@ -9,7 +9,8 @@ ENGINE="Unity 6 (6000.0.62f1), Mono backend, URP + Cinemachine" # Fix method: BepInEx plugin (not SUWSF — no DRM, managed C# assemblies) METHOD="bepinex" LOADER="winhttp.dll" # BepInEx's Doorstop proxy +PLUGIN_DLL="BurglinGnomes.UltraWideFix.dll" LAUNCH_OPTION='WINEDLLOVERRIDES="winhttp=n,b" %command%' PREFIX_INI="" # no config-level tweak identified yet -STATUS="IN PROGRESS — symptoms: camera zoomed (Vert-) + broken UI at 32:9" +STATUS="Confirmed working at 5120x1440 (32:9) — HUD + nameplate scaling" diff --git a/games/BurglinGnomes/plugin/UltraWideFix.cs b/games/BurglinGnomes/plugin/UltraWideFix.cs index 24e29e9..570b0b3 100644 --- a/games/BurglinGnomes/plugin/UltraWideFix.cs +++ b/games/BurglinGnomes/plugin/UltraWideFix.cs @@ -57,14 +57,14 @@ namespace BurglinGnomes.UltraWideFix "Keep at 1 for ultrawide; lower values re-introduce width scaling and " + "cramp the layout vertically.", new AcceptableValueRange(0f, 1f))); - _hudScale = Config.Bind("HUD", "HudScale", 0.85f, + _hudScale = Config.Bind("HUD", "HudScale", 0.8f, new ConfigDescription( "HUD size multiplier. Below 1 shrinks the HUD AND gives the layout more " + "logical room, which is what cures overlapping text. 1 = stock size.", new AcceptableValueRange(0.4f, 1.5f))); _designHeight = Config.Bind("HUD", "DesignHeight", 1080f, "The game's design reference height. Do not change unless you know why."); - _worldScale = Config.Bind("World", "WorldLabelScale", 1.0f, + _worldScale = Config.Bind("World", "WorldLabelScale", 0.8f, new ConfigDescription( "Size multiplier for in-world labels (nameplates). At 32:9 you see twice " + "as much world, so more labels are on screen and can collide; lower this " + diff --git a/install.sh b/install.sh index 0841c76..439f956 100755 --- a/install.sh +++ b/install.sh @@ -105,32 +105,58 @@ remove_ini() { fi } +METHOD="${METHOD:-suwsf}" + if [ "$ACTION" = "uninstall" ]; then echo "[+] Uninstalling..." - for f in SUWSF.asi SUWSF.ini dsound.dll dinput8.dll version.dll winmm.dll; do - [ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f" - done + if [ "$METHOD" = "bepinex" ]; then + rm -vf "$GAMEDIR/winhttp.dll" "$GAMEDIR/doorstop_config.ini" "$GAMEDIR/.doorstop_version" \ + "$GAMEDIR/changelog.txt" "$GAMEDIR/BepInEx/plugins/$PLUGIN_DLL" + # leave BepInEx/ itself: the user may have other mods installed + echo "[i] Left BepInEx/ in place (other mods may rely on it). Delete it manually if unused." + else + for f in SUWSF.asi SUWSF.ini dsound.dll dinput8.dll version.dll winmm.dll; do + [ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f" + done + fi remove_ini echo "[+] Done. No game file was ever modified." exit 0 fi # --- install ------------------------------------------------------------------ -ASI="$HERE/vendor/SUWSF-x64/SUWSF.asi" -LOADER_SRC="$HERE/vendor/SUWSF-x64/dsound.dll" # Ultimate ASI Loader (adapts by filename) -INI="$GAMES_DIR/$GAME/SUWSF.ini" -for f in "$ASI" "$LOADER_SRC" "$INI"; do - [ -f "$f" ] || { echo "[!] missing $f"; exit 1; } -done +if [ "$METHOD" = "bepinex" ]; then + BEPZIP=$(ls "$HERE"/vendor/BepInEx_win_x64_*.zip 2>/dev/null | head -1) + PLUGIN="$GAMES_DIR/$GAME/$PLUGIN_DLL" + [ -f "$BEPZIP" ] || { echo "[!] missing BepInEx zip in vendor/"; exit 1; } + [ -f "$PLUGIN" ] || { echo "[!] missing prebuilt plugin: $PLUGIN"; exit 1; } -cp -v "$ASI" "$GAMEDIR/SUWSF.asi" -cp -v "$INI" "$GAMEDIR/SUWSF.ini" -cp -v "$LOADER_SRC" "$GAMEDIR/$LOADER" + if [ -f "$GAMEDIR/winhttp.dll" ]; then + echo "[i] BepInEx already present; updating plugin only." + else + command -v unzip >/dev/null || { echo "[!] unzip required"; exit 1; } + unzip -o -q "$BEPZIP" -d "$GAMEDIR" + echo "[+] Installed BepInEx ($(basename "$BEPZIP"))" + fi + mkdir -p "$GAMEDIR/BepInEx/plugins" + cp -v "$PLUGIN" "$GAMEDIR/BepInEx/plugins/" +else + ASI="$HERE/vendor/SUWSF-x64/SUWSF.asi" + LOADER_SRC="$HERE/vendor/SUWSF-x64/dsound.dll" # Ultimate ASI Loader (adapts by filename) + INI="$GAMES_DIR/$GAME/SUWSF.ini" + for f in "$ASI" "$LOADER_SRC" "$INI"; do + [ -f "$f" ] || { echo "[!] missing $f"; exit 1; } + done + + cp -v "$ASI" "$GAMEDIR/SUWSF.asi" + cp -v "$INI" "$GAMEDIR/SUWSF.ini" + cp -v "$LOADER_SRC" "$GAMEDIR/$LOADER" +fi apply_ini cat < right-click ${TITLE:-$GAME} > Properties > Launch Options: @@ -138,6 +164,12 @@ NEXT (Proton) — REQUIRED, or the loader is ignored and the fix won't apply in Then set the in-game resolution to your native ultrawide res, fullscreen. +Tuning (METHOD=bepinex games): + Config is generated on first launch at + /BepInEx/config/*.cfg (edits apply live, ~2s, no restart) + Plugin log (proves whether it applied): + /BepInEx/LogOutput.log + Troubleshooting: Different loader name: LOADER_NAME=version.dll ./install.sh $GAME Diagnose injection: add PROTON_LOG=1 to launch options, then