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 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 16:33:56 -04:00
co-authored by Claude Opus 4.8
parent ed2e0016ba
commit 59ee13de9d
6 changed files with 162 additions and 23 deletions
+15 -7
View File
@@ -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**. 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 ## Supported games
| Game | Engine | Status | | Game | Engine | Method | Status |
|---|---|---| |---|---|---|---|
| [Gundam Breaker 4](games/GundamBreaker4/) | Unreal Engine 4.27 | ✅ Confirmed working in gameplay at 5120×1440 | | [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 ## Install
@@ -32,7 +38,8 @@ Config-file tweaks (e.g. UE4's `AspectRatioAxisConstraint=MaintainYFOV`) typical
| Path | What | | Path | What |
|---|---| |---|---|
| `games/<Game>/SUWSF.ini` | That game's patch definitions | | `games/<Game>/SUWSF.ini` | Patch definitions (SUWSF games) |
| `games/<Game>/plugin/` | C# plugin source + prebuilt DLL (BepInEx games) |
| `games/<Game>/game.conf` | Game metadata (appid, exe, paths, launch option) | | `games/<Game>/game.conf` | Game metadata (appid, exe, paths, launch option) |
| `install.sh` | Generic installer / uninstaller | | `install.sh` | Generic installer / uninstaller |
| `tools/build_release.sh` | Builds a per-game drop-in release zip | | `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 ## Adding a game
1. `mkdir games/<GameName>` with a `game.conf` (copy GB4's as a template) and a `SUWSF.ini`. 1. `mkdir games/<GameName>` 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. 2. Set `METHOD=` in `game.conf` to `suwsf` or `bepinex`.
3. If a pattern misses, use `tools/dump_decrypted.py` + `tools/aob.py` to derive the game's exact bytes. 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 ## License
Binary file not shown.
+98
View File
@@ -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:
```
<game>/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
```
<game>/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.
+2 -1
View File
@@ -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) # Fix method: BepInEx plugin (not SUWSF — no DRM, managed C# assemblies)
METHOD="bepinex" METHOD="bepinex"
LOADER="winhttp.dll" # BepInEx's Doorstop proxy LOADER="winhttp.dll" # BepInEx's Doorstop proxy
PLUGIN_DLL="BurglinGnomes.UltraWideFix.dll"
LAUNCH_OPTION='WINEDLLOVERRIDES="winhttp=n,b" %command%' LAUNCH_OPTION='WINEDLLOVERRIDES="winhttp=n,b" %command%'
PREFIX_INI="" # no config-level tweak identified yet 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"
+2 -2
View File
@@ -57,14 +57,14 @@ namespace BurglinGnomes.UltraWideFix
"Keep at 1 for ultrawide; lower values re-introduce width scaling and " + "Keep at 1 for ultrawide; lower values re-introduce width scaling and " +
"cramp the layout vertically.", "cramp the layout vertically.",
new AcceptableValueRange<float>(0f, 1f))); new AcceptableValueRange<float>(0f, 1f)));
_hudScale = Config.Bind("HUD", "HudScale", 0.85f, _hudScale = Config.Bind("HUD", "HudScale", 0.8f,
new ConfigDescription( new ConfigDescription(
"HUD size multiplier. Below 1 shrinks the HUD AND gives the layout more " + "HUD size multiplier. Below 1 shrinks the HUD AND gives the layout more " +
"logical room, which is what cures overlapping text. 1 = stock size.", "logical room, which is what cures overlapping text. 1 = stock size.",
new AcceptableValueRange<float>(0.4f, 1.5f))); new AcceptableValueRange<float>(0.4f, 1.5f)));
_designHeight = Config.Bind("HUD", "DesignHeight", 1080f, _designHeight = Config.Bind("HUD", "DesignHeight", 1080f,
"The game's design reference height. Do not change unless you know why."); "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( new ConfigDescription(
"Size multiplier for in-world labels (nameplates). At 32:9 you see twice " + "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 " + "as much world, so more labels are on screen and can collide; lower this " +
+42 -10
View File
@@ -105,32 +105,58 @@ remove_ini() {
fi fi
} }
METHOD="${METHOD:-suwsf}"
if [ "$ACTION" = "uninstall" ]; then if [ "$ACTION" = "uninstall" ]; then
echo "[+] Uninstalling..." echo "[+] Uninstalling..."
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 for f in SUWSF.asi SUWSF.ini dsound.dll dinput8.dll version.dll winmm.dll; do
[ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f" [ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f"
done done
fi
remove_ini remove_ini
echo "[+] Done. No game file was ever modified." echo "[+] Done. No game file was ever modified."
exit 0 exit 0
fi fi
# --- install ------------------------------------------------------------------ # --- install ------------------------------------------------------------------
ASI="$HERE/vendor/SUWSF-x64/SUWSF.asi" if [ "$METHOD" = "bepinex" ]; then
LOADER_SRC="$HERE/vendor/SUWSF-x64/dsound.dll" # Ultimate ASI Loader (adapts by filename) BEPZIP=$(ls "$HERE"/vendor/BepInEx_win_x64_*.zip 2>/dev/null | head -1)
INI="$GAMES_DIR/$GAME/SUWSF.ini" PLUGIN="$GAMES_DIR/$GAME/$PLUGIN_DLL"
for f in "$ASI" "$LOADER_SRC" "$INI"; do [ -f "$BEPZIP" ] || { echo "[!] missing BepInEx zip in vendor/"; exit 1; }
[ -f "$f" ] || { echo "[!] missing $f"; exit 1; } [ -f "$PLUGIN" ] || { echo "[!] missing prebuilt plugin: $PLUGIN"; exit 1; }
done
cp -v "$ASI" "$GAMEDIR/SUWSF.asi" if [ -f "$GAMEDIR/winhttp.dll" ]; then
cp -v "$INI" "$GAMEDIR/SUWSF.ini" echo "[i] BepInEx already present; updating plugin only."
cp -v "$LOADER_SRC" "$GAMEDIR/$LOADER" 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 apply_ini
cat <<EOF cat <<EOF
[+] Installed: SUWSF.asi, SUWSF.ini, $LOADER [+] Installed ($METHOD): $([ "$METHOD" = "bepinex" ] && echo "BepInEx + $PLUGIN_DLL + $LOADER" || echo "SUWSF.asi, SUWSF.ini, $LOADER")
NEXT (Proton) — REQUIRED, or the loader is ignored and the fix won't apply in gameplay. NEXT (Proton) — REQUIRED, or the loader is ignored and the fix won't apply in gameplay.
Steam > right-click ${TITLE:-$GAME} > Properties > Launch Options: Steam > 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. Then set the in-game resolution to your native ultrawide res, fullscreen.
Tuning (METHOD=bepinex games):
Config is generated on first launch at
<game>/BepInEx/config/*.cfg (edits apply live, ~2s, no restart)
Plugin log (proves whether it applied):
<game>/BepInEx/LogOutput.log
Troubleshooting: Troubleshooting:
Different loader name: LOADER_NAME=version.dll ./install.sh $GAME Different loader name: LOADER_NAME=version.dll ./install.sh $GAME
Diagnose injection: add PROTON_LOG=1 to launch options, then Diagnose injection: add PROTON_LOG=1 to launch options, then