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 0000000..e0648e8 Binary files /dev/null and b/games/BurglinGnomes/BurglinGnomes.UltraWideFix.dll differ 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