Compare commits
18
Commits
9bdb4284d6
...
v1.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1176df8f20 | ||
|
|
15d0169f56 | ||
|
|
6f24502956 | ||
|
|
0833cf8495 | ||
|
|
efaab8bd32 | ||
|
|
b97a8a73b1 | ||
|
|
59ee13de9d | ||
|
|
ed2e0016ba | ||
|
|
40d057ac6e | ||
|
|
cbcc53c2e9 | ||
|
|
b9e68fd284 | ||
|
|
ae994c27e6 | ||
|
|
4cd18fb9f4 | ||
|
|
bd9fdc50f6 | ||
|
|
3525c79dba | ||
|
|
8bbc1cfac8 | ||
|
|
e21c6c3c44 | ||
|
|
b6ab1bf5ab |
@@ -14,3 +14,7 @@ __pycache__/
|
||||
gb4dump/
|
||||
*.bin
|
||||
*.bak-bg4uw
|
||||
|
||||
# .NET plugin build output
|
||||
**/plugin/bin/
|
||||
**/plugin/obj/
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
Versioning applies to the collection as a whole: **adding a game is a minor bump**,
|
||||
fixes/tuning to existing entries are patch bumps.
|
||||
|
||||
## v1.2.0 — Clair Obscur: Expedition 33
|
||||
|
||||
- **New game: Clair Obscur: Expedition 33** (Unreal Engine 5, no DRM) — ⚠️ partial support.
|
||||
Adds a third install method, `external`: where a good third-party fix already
|
||||
exists and its license allows redistribution, it is vendored **unmodified**
|
||||
with our tuned config and full credit, rather than reimplemented.
|
||||
The fix itself is [ClairObscurFix](https://github.com/Lyall/ClairObscurFix)
|
||||
by **Lyall** (MIT).
|
||||
- Ships a **reduced profile** (only `Center HUD` enabled). Upstream v0.0.10 is
|
||||
archived and predates current game builds; with full features it crashed on
|
||||
save load. Documented with a revalidation checklist rather than marked working.
|
||||
- `publish.sh` now cuts **one release for the whole collection**, attaching every
|
||||
game's zip, and replaces same-named assets so re-runs are idempotent.
|
||||
|
||||
## v1.1.0 — Burglin' Gnomes *(never released; folded into v1.2.0)*
|
||||
|
||||
- **New game: Burglin' Gnomes** (Unity 6, Mono) — ✅ confirmed working at 5120×1440.
|
||||
Adds the `bepinex` install method and a purpose-built C# plugin.
|
||||
- Camera deliberately untouched: it is orthographic with a constant
|
||||
`orthographicSize`, which is already correct Hor+ at 32:9. The entire problem
|
||||
was UI scaling.
|
||||
- Fixes HUD canvases (`match=1` + raised `referenceResolution.y`) and world-space
|
||||
labels (canvas `localScale` — `CanvasScaler.scaleFactor` is overwritten every
|
||||
frame for WorldSpace canvases).
|
||||
- Config reloads live (~2s) without a game restart.
|
||||
|
||||
## v1.0.0 — Gundam Breaker 4
|
||||
|
||||
- **New game: Gundam Breaker 4** (Unreal Engine 4.27, SteamStub DRM) — ✅ confirmed
|
||||
working at 5120×1440.
|
||||
- Forces Hor+ FOV via `AspectRatioAxisConstraint = MaintainYFOV` and disables
|
||||
pillarboxing, patched in decrypted memory at runtime with
|
||||
[SUWSF](https://github.com/PhantomGamers/SUWSF) — the exe is DRM-encrypted on
|
||||
disk, so static patching is impossible.
|
||||
|
||||
---
|
||||
|
||||
### Unreleased infrastructure changes
|
||||
|
||||
These landed between v1.0.0 and v1.2.0 and are not tied to a single game:
|
||||
|
||||
- Restructured from a single-game repo into a multi-game collection
|
||||
(`games/<Game>/` + `game.conf`), so `install.sh` is game-agnostic.
|
||||
**Note:** `./install.sh` now requires a game name; run it bare to list games.
|
||||
- Migrated hosting from GitHub to self-hosted Gitea.
|
||||
- `publish.sh` rewritten for the Gitea API; reads `GITEA_TOKEN` from the
|
||||
environment and never stores it.
|
||||
@@ -2,13 +2,21 @@
|
||||
|
||||
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.
|
||||
- **External** — where a good third-party fix already exists, it is vendored (license permitting) with our tuned config and full credit, rather than reimplemented. Vendored fixes can go stale against a patched game, so those entries carry a revalidation checklist.
|
||||
|
||||
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 |
|
||||
| [Clair Obscur: Expedition 33](games/Expedition33/) | Unreal Engine 5 | External ([ClairObscurFix](https://github.com/Lyall/ClairObscurFix) by Lyall) | ⚠️ Partial — reduced profile, see notes |
|
||||
|
||||
## Install
|
||||
|
||||
@@ -32,21 +40,32 @@ Config-file tweaks (e.g. UE4's `AspectRatioAxisConstraint=MaintainYFOV`) typical
|
||||
|
||||
| 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>/*.ini` | Our tuned config for a vendored external fix |
|
||||
| `games/<Game>/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 |
|
||||
| `tools/publish.sh` | Pushes + cuts a GitHub release |
|
||||
| `tools/publish.sh` | Pushes + cuts a Gitea release |
|
||||
| `tools/dump_decrypted.py` | Dumps a DRM-decrypted image from a running process |
|
||||
| `tools/aob.py` | Verifies/derives byte patterns against a dump |
|
||||
| `tools/read_protonlog.sh` | Confirms ASI injection + per-patch match counts |
|
||||
| `tools/unity_diag.cs`, `tools/unity_uitune.cs` | UnityExplorer console diagnostics/tuners |
|
||||
| `vendor/SUWSF-x64/` | Upstream SUWSF 2.3.0 + Ultimate ASI Loader (MIT) |
|
||||
| `vendor/BepInEx_win_x64_*.zip` | Upstream BepInEx 5.4.23.5 (LGPL-2.1) |
|
||||
|
||||
## Adding a game
|
||||
|
||||
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.
|
||||
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`, `bepinex`, or `external`.
|
||||
**Check for an existing fix first** — if a good one exists and its license
|
||||
allows redistribution, vendor it with credit instead of rewriting it.
|
||||
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/`).
|
||||
|
||||
## Changelog
|
||||
|
||||
Version history and the versioning policy (adding a game = minor bump) are in [CHANGELOG.md](CHANGELOG.md).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -17,6 +17,29 @@ them; all runtime patching is performed by SUWSF.
|
||||
- License: MIT
|
||||
- Files: `vendor/SUWSF-x64/dsound.dll` (renamed proxy; adapts by filename)
|
||||
|
||||
## BepInEx
|
||||
- Author: BepInEx contributors
|
||||
- Source: https://github.com/BepInEx/BepInEx
|
||||
- License: LGPL-2.1
|
||||
- Files: `vendor/BepInEx_win_x64_5.4.23.5.zip` (redistributed unmodified)
|
||||
- Used by Unity games (e.g. Burglin' Gnomes) to load the C# fix plugin.
|
||||
|
||||
## UnityExplorer (development only)
|
||||
- Author: sinai-dev
|
||||
- Source: https://github.com/sinai-dev/UnityExplorer
|
||||
- License: GPL-3.0
|
||||
- Files: `vendor/UnityExplorer.BepInEx5.Mono.zip`
|
||||
- Used only for runtime inspection while developing a fix; it is NOT part of any
|
||||
release package and is not required by end users.
|
||||
|
||||
## ClairObscurFix (the entire Expedition 33 fix)
|
||||
- Author: **Lyall** — https://github.com/Lyall/ClairObscurFix
|
||||
- License: MIT (see `vendor/ClairObscurFix/LICENSE`)
|
||||
- Files: `vendor/ClairObscurFix/**` — vendored **unmodified**
|
||||
- Version: v0.0.10 (upstream repo is archived)
|
||||
- The ultrawide fix for Clair Obscur: Expedition 33 is entirely Lyall's work.
|
||||
This repo contributes only a 32:9-tuned `ClairObscurFix.ini` and packaging.
|
||||
|
||||
## Patch patterns
|
||||
The `AspectRatioAxisConstraint` (HOR+) and `ConstrainAspectRatio` (pillarbox)
|
||||
byte patterns in `games/*/SUWSF.ini` are adapted from the UE4 fixes in
|
||||
|
||||
Binary file not shown.
@@ -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.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Burglin' Gnomes — metadata for ../../install.sh
|
||||
# NOTE: this game uses a DIFFERENT fix method than the SUWSF/UE4 games.
|
||||
TITLE="Burglin' Gnomes"
|
||||
APPID=3844970
|
||||
EXE="Gnomium.exe"
|
||||
COMMON_SUBDIR="Burglin' Gnomes"
|
||||
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="Confirmed working at 5120x1440 (32:9) — HUD + nameplate scaling"
|
||||
@@ -0,0 +1,177 @@
|
||||
using BepInEx;
|
||||
using BepInEx.Configuration;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace BurglinGnomes.UltraWideFix
|
||||
{
|
||||
/// <summary>
|
||||
/// Ultrawide UI fix for Burglin' Gnomes (Unity 6, Mono).
|
||||
///
|
||||
/// The camera needs no patching: it is orthographic with a constant
|
||||
/// orthographicSize (5.0), so a wider display already shows proportionally
|
||||
/// more world (visible width 17.8 -> 35.6 going 16:9 -> 32:9). That is
|
||||
/// correct Hor+ behaviour.
|
||||
///
|
||||
/// The actual problem is UI scaling:
|
||||
/// * HUD canvases (ScreenSpaceOverlay + ScaleWithScreenSize) ship
|
||||
/// matchWidthOrHeight = 0.453, which is width-biased. At 5120px wide that
|
||||
/// yields scaleFactor 1.948 and a ballooned HUD. Matching HEIGHT (1.0)
|
||||
/// gives 1.333, and raising referenceResolution.y shrinks it further
|
||||
/// while giving the layout MORE logical room (cures overlapping text).
|
||||
/// * World-space label canvases (NamePlate, ...) are NOT affected by
|
||||
/// CanvasScaler.scaleFactor - CanvasScaler.HandleWorldCanvas() overwrites
|
||||
/// it with dynamicPixelsPerUnit every frame. Their size comes from the
|
||||
/// canvas transform's localScale, so that is what we scale.
|
||||
/// </summary>
|
||||
[BepInPlugin(PluginGuid, PluginName, PluginVersion)]
|
||||
public class UltraWideFixPlugin : BaseUnityPlugin
|
||||
{
|
||||
public const string PluginGuid = "com.programmingpug.burglingnomes.ultrawidefix";
|
||||
public const string PluginName = "Burglin' Gnomes UltraWide Fix";
|
||||
public const string PluginVersion = "1.0.0";
|
||||
|
||||
private ConfigEntry<bool> _enabled;
|
||||
private ConfigEntry<float> _match;
|
||||
private ConfigEntry<float> _hudScale;
|
||||
private ConfigEntry<float> _worldScale;
|
||||
private ConfigEntry<float> _designHeight;
|
||||
private ConfigEntry<bool> _verbose;
|
||||
|
||||
// Baselines captured once per canvas so repeated applies never compound.
|
||||
private readonly System.Collections.Generic.Dictionary<int, Vector2> _refBaseline
|
||||
= new System.Collections.Generic.Dictionary<int, Vector2>();
|
||||
private readonly System.Collections.Generic.Dictionary<int, Vector3> _scaleBaseline
|
||||
= new System.Collections.Generic.Dictionary<int, Vector3>();
|
||||
|
||||
private float _timer;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_enabled = Config.Bind("General", "Enabled", true,
|
||||
"Master switch for the ultrawide UI fix.");
|
||||
_match = Config.Bind("HUD", "MatchWidthOrHeight", 1.0f,
|
||||
new ConfigDescription(
|
||||
"CanvasScaler match for HUD canvases. 0 = scale by width, 1 = by height. " +
|
||||
"Keep at 1 for ultrawide; lower values re-introduce width scaling and " +
|
||||
"cramp the layout vertically.",
|
||||
new AcceptableValueRange<float>(0f, 1f)));
|
||||
_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<float>(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", 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 " +
|
||||
"to shrink them. 1 = stock size.",
|
||||
new AcceptableValueRange<float>(0.3f, 1.5f)));
|
||||
_verbose = Config.Bind("Debug", "Verbose", false,
|
||||
"Log every canvas adjustment.");
|
||||
|
||||
SceneManager.sceneLoaded += (_, __) => Apply();
|
||||
Logger.LogInfo($"{PluginName} {PluginVersion} loaded.");
|
||||
}
|
||||
|
||||
private void Start() => Apply();
|
||||
|
||||
// Canvases are created lazily (menus, popups), so re-apply periodically.
|
||||
// We also re-read the config file each tick: BepInEx does NOT pick up
|
||||
// external edits on its own, so without this you'd have to restart the
|
||||
// game after every tweak.
|
||||
private void Update()
|
||||
{
|
||||
_timer += Time.unscaledDeltaTime;
|
||||
if (_timer < 2f) return;
|
||||
_timer = 0f;
|
||||
|
||||
try
|
||||
{
|
||||
var stamp = System.IO.File.GetLastWriteTimeUtc(Config.ConfigFilePath);
|
||||
if (stamp != _configStamp)
|
||||
{
|
||||
_configStamp = stamp;
|
||||
Config.Reload();
|
||||
Logger.LogInfo($"Config reloaded: HudScale={_hudScale.Value} " +
|
||||
$"WorldLabelScale={_worldScale.Value} " +
|
||||
$"Match={_match.Value} Enabled={_enabled.Value}");
|
||||
// values changed -> re-derive from baselines on this pass
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Logger.LogWarning($"Config reload failed (check the .cfg syntax): {e.Message}");
|
||||
}
|
||||
|
||||
Apply();
|
||||
}
|
||||
|
||||
private System.DateTime _configStamp;
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
if (!_enabled.Value) return;
|
||||
|
||||
foreach (var cs in FindObjectsOfType<CanvasScaler>())
|
||||
{
|
||||
var canvas = cs.GetComponent<Canvas>();
|
||||
if (canvas == null) continue;
|
||||
int id = cs.GetInstanceID();
|
||||
|
||||
if (canvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
if (Mathf.Approximately(_worldScale.Value, 1f)) continue;
|
||||
|
||||
var cur = cs.transform.localScale;
|
||||
// Games hide world-space UI by zeroing its scale. Never cache a
|
||||
// zero baseline: if we did, multiplying it would pin the canvas
|
||||
// to zero forever and the UI could never reappear.
|
||||
if (!_scaleBaseline.TryGetValue(id, out var base3))
|
||||
{
|
||||
if (cur.x <= 0.0001f) continue; // hidden right now; revisit later
|
||||
base3 = cur;
|
||||
_scaleBaseline[id] = base3;
|
||||
}
|
||||
// Respect the game hiding it: leave zeroed canvases alone.
|
||||
if (cur.x <= 0.0001f) continue;
|
||||
|
||||
var want = base3 * _worldScale.Value;
|
||||
if (cs.transform.localScale != want)
|
||||
{
|
||||
cs.transform.localScale = want;
|
||||
if (_verbose.Value)
|
||||
Logger.LogInfo($"World '{cs.name}' localScale {base3} -> {want}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cs.uiScaleMode != CanvasScaler.ScaleMode.ScaleWithScreenSize) continue;
|
||||
|
||||
if (!_refBaseline.TryGetValue(id, out var baseRef))
|
||||
{
|
||||
baseRef = cs.referenceResolution;
|
||||
_refBaseline[id] = baseRef;
|
||||
}
|
||||
|
||||
float targetH = _designHeight.Value / Mathf.Max(0.01f, _hudScale.Value);
|
||||
var targetRef = new Vector2(baseRef.x, targetH);
|
||||
|
||||
if (!Mathf.Approximately(cs.matchWidthOrHeight, _match.Value))
|
||||
cs.matchWidthOrHeight = _match.Value;
|
||||
if (cs.referenceResolution != targetRef)
|
||||
{
|
||||
cs.referenceResolution = targetRef;
|
||||
// expected scale now; Canvas.scaleFactor lags a frame
|
||||
float exp = Mathf.Pow(Screen.width / targetRef.x, 1f - _match.Value) *
|
||||
Mathf.Pow(Screen.height / targetRef.y, _match.Value);
|
||||
Logger.LogInfo($"HUD '{cs.name}' ref {baseRef} -> {targetRef} " +
|
||||
$"match={_match.Value} => scale={exp:F3}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AssemblyName>BurglinGnomes.UltraWideFix</AssemblyName>
|
||||
<RootNamespace>BurglinGnomes.UltraWideFix</RootNamespace>
|
||||
<Version>1.0.0</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<!-- Plugin references the game's own assemblies; nothing is redistributed. -->
|
||||
<AssemblySearchPaths>$(AssemblySearchPaths);$(GameManaged)</AssemblySearchPaths>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<NoWarn>$(NoWarn);CS0436</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="UltraWideFix.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
GameDir must point at the Burglin' Gnomes install. Override on the CLI:
|
||||
dotnet build -c Release -p:GameDir="/path/to/Burglin' Gnomes"
|
||||
-->
|
||||
<PropertyGroup>
|
||||
<GameDir Condition="'$(GameDir)' == ''">/media/ckoch/Data/steam/steamapps/common/Burglin' Gnomes</GameDir>
|
||||
<GameManaged>$(GameDir)/Gnomium_Data/Managed</GameManaged>
|
||||
<BepInExCore>$(GameDir)/BepInEx/core</BepInExCore>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="BepInEx">
|
||||
<HintPath>$(BepInExCore)/BepInEx.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>$(BepInExCore)/0Harmony.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>$(GameManaged)/UnityEngine.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>$(GameManaged)/UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UI">
|
||||
<HintPath>$(GameManaged)/UnityEngine.UI.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UIModule">
|
||||
<HintPath>$(GameManaged)/UnityEngine.UIModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,52 @@
|
||||
;;;;;;;;;; General ;;;;;;;;;;
|
||||
|
||||
[Developer Console]
|
||||
; Set "Enabled" to true to enable the console.
|
||||
; By default you can access it with the key "Tilde".
|
||||
Enabled = false
|
||||
|
||||
[Skip Intro Logos]
|
||||
; Skips splash screen logos, epilepsy warning and autosave warning.
|
||||
Enabled = false
|
||||
|
||||
[Uncap Cutscene FPS]
|
||||
; Removes 30fps cap in cutscenes.
|
||||
; DISABLED: bisecting a crash-on-save-load with game build > fix v0.0.10.
|
||||
Enabled = false
|
||||
|
||||
[Adjust Resolution Checks]
|
||||
; Adjusts resolution checks to use the monitor's maximum supported resolution, rather than the "native" resolution.
|
||||
; DISABLED: its pattern scan already fails on this game build ("Resolution Checks: Pattern scan failed").
|
||||
Enabled = false
|
||||
|
||||
[Background Audio]
|
||||
; Set "Enabled" to true to allow audio to continue playing when the game window has lost focus.
|
||||
Enabled = false
|
||||
|
||||
;;;;;;;;;; Ultrawide/Narrower ;;;;;;;;;;
|
||||
|
||||
[Cutscene Letterboxing]
|
||||
; Set "Enabled" to true to re-enable pillarboxing/letterboxing in cutscenes.
|
||||
Enabled = false
|
||||
|
||||
[Fix Movies]
|
||||
; Crops letterboxing from pre-rendered movies at ultrawide resolutions.
|
||||
; DISABLED: bisecting; re-enable once the crash source is known.
|
||||
Enabled = false
|
||||
|
||||
[Center HUD]
|
||||
; Set "Enabled" to true to center certain parts of the HUD to 16:9 at ultrawide resolutions.
|
||||
Enabled = true
|
||||
; Adjust "AspectRatio" to set the aspect ratio of the HUD. i.e 1.77778 for 16:9, 2.33333 for 21:9 etc
|
||||
AspectRatio = 1.77778
|
||||
|
||||
;;;;;;;;;; Graphics ;;;;;;;;;;
|
||||
|
||||
[Disable Subtitle Blur]
|
||||
; Set "Enabled" to true to disable the background blur on cutscene subtitle lines.
|
||||
Enabled = false
|
||||
|
||||
[Sharpening]
|
||||
; Adjust "Strength" to control the intensity of the forced post-process sharpening.
|
||||
; Valid range: 0 to 2.0 Game Default = 1.0
|
||||
Strength = 0
|
||||
@@ -0,0 +1,101 @@
|
||||
# Clair Obscur: Expedition 33 — UltraWide
|
||||
|
||||
⚠️ **Partial support — reduced feature profile.** ClairObscurFix v0.0.10 is archived and predates the current game build; running it with upstream defaults **crashed on save load**. This repo ships a reduced profile with only the 32:9 HUD centering enabled.
|
||||
|
||||
- **Engine:** Unreal Engine 5 — **no DRM** (no SteamStub; `.text` entropy 6.74)
|
||||
- **Steam AppID:** 1903340
|
||||
- **Executable:** `SandFall-Win64-Shipping.exe`
|
||||
- **Method:** `external` — vendored third-party fix + our 32:9 config
|
||||
|
||||
## We did not write this fix
|
||||
|
||||
The ultrawide work here is **[ClairObscurFix](https://github.com/Lyall/ClairObscurFix) by [Lyall](https://github.com/Lyall)** (MIT), vendored unmodified. It is mature and already handles the hard parts; reimplementing it would be duplicated effort for no benefit. **All credit for the fix itself goes to Lyall.**
|
||||
|
||||
What this repo adds is the **32:9 configuration** verified on a Samsung 49" G93SC, packaging, and a one-command installer consistent with the other games here.
|
||||
|
||||
> The upstream repo is **archived**, so it will not receive further updates. Vendoring it here means the version you need stays available.
|
||||
|
||||
## Install (Linux / Proton)
|
||||
|
||||
```bash
|
||||
./install.sh Expedition33
|
||||
```
|
||||
|
||||
Then **required** — Steam → Properties → Launch Options:
|
||||
|
||||
```
|
||||
WINEDLLOVERRIDES="dsound=n,b" %command%
|
||||
```
|
||||
|
||||
Set **5120×1440** fullscreen in-game.
|
||||
|
||||
## Install (Windows)
|
||||
|
||||
Extract the release zip into `…\Expedition 33\Sandfall\Binaries\Win64\`.
|
||||
|
||||
## Our 32:9 config
|
||||
|
||||
`ClairObscurFix.ini` ships with upstream defaults **except**:
|
||||
|
||||
| Option | Upstream | Ours | Why |
|
||||
|---|---|---|---|
|
||||
| `[Center HUD] Enabled` | `false` | **`true`** | At 5120px wide, HUD elements anchored to screen edges end up absurdly far apart |
|
||||
|
||||
Everything else is left at upstream defaults, which are already ultrawide-friendly:
|
||||
|
||||
| Option | Default | Effect |
|
||||
|---|---|---|
|
||||
| `Cutscene Letterboxing` | `false` | No bars in cutscenes |
|
||||
| `Fix Movies` | `true` | Crops letterboxing from pre-rendered video |
|
||||
| `Adjust Resolution Checks` | `true` | Uses the monitor's max resolution rather than "native" |
|
||||
| `Uncap Cutscene FPS` | `true` | Removes the 30fps cutscene cap |
|
||||
|
||||
### Tuning
|
||||
|
||||
Edit `…/Sandfall/Binaries/Win64/ClairObscurFix.ini` (requires a restart — this fix has no live reload):
|
||||
|
||||
- **HUD too cramped?** Raise `[Center HUD] AspectRatio` from `1.77778` (16:9) to `2.33333` (21:9) — usually the sweet spot at 32:9.
|
||||
- **Prefer the HUD spanning the full width?** Set `[Center HUD] Enabled = false`.
|
||||
- **Want cinematic bars back?** `[Cutscene Letterboxing] Enabled = true`.
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
./install.sh Expedition33 uninstall
|
||||
```
|
||||
|
||||
## Known issue: version drift
|
||||
|
||||
**ClairObscurFix v0.0.10 (May 2025) is archived and no longer tracks the game.**
|
||||
|
||||
Observed on a current build (2026-07-21), with upstream defaults:
|
||||
|
||||
```
|
||||
[error] Resolution Checks: Pattern scan failed.
|
||||
[error] Resolution Scale Bug: Pattern scan failed.
|
||||
```
|
||||
|
||||
…followed by a **crash when loading a save**. Removing the fix entirely made the
|
||||
game load normally, isolating the fix as the cause. The crash coincides with the
|
||||
fix's `Level Load` hook, which is consistent with a hook attaching to shifted
|
||||
code in a newer binary.
|
||||
|
||||
**Mitigation:** ship a reduced profile — everything disabled except `Center HUD`,
|
||||
the only option genuinely needed at 32:9. The failing scans are disabled outright
|
||||
since they were already inert.
|
||||
|
||||
## Maintenance / revalidation
|
||||
|
||||
Because upstream is archived, this entry decays as the game patches. Re-check
|
||||
after any significant game update:
|
||||
|
||||
1. Launch and load a save. If it crashes, remove the fix (`./install.sh Expedition33 uninstall`) and retry to confirm the fix is the cause rather than the game or Proton.
|
||||
2. Read `Sandfall/Binaries/Win64/ClairObscurFix.log` — `Pattern scan failed` lines mark hooks that no longer match the binary.
|
||||
3. Disable any newly-failing feature in `ClairObscurFix.ini`; a failed scan is inert, but a *wrongly-attached* hook is what crashes.
|
||||
4. If `Center HUD` itself stops working, this entry should be marked unsupported. The game has **no DRM**, so writing a small dedicated HUD-centering patch is a viable replacement.
|
||||
|
||||
## Still unverified at 32:9
|
||||
|
||||
- [ ] Is `Center HUD` at 16:9 right, or is 21:9 (`2.33333`) better?
|
||||
- [ ] Camera FOV — Hor+ (see more) or Vert- (zoomed)?
|
||||
- [ ] Pillarboxing in cutscenes / pre-rendered movies (`Fix Movies` currently **off**)
|
||||
@@ -0,0 +1,19 @@
|
||||
# Clair Obscur: Expedition 33 — metadata for ../../install.sh
|
||||
TITLE="Clair Obscur: Expedition 33"
|
||||
APPID=1903340
|
||||
|
||||
EXE="SandFall-Win64-Shipping.exe"
|
||||
COMMON_SUBDIR="Expedition 33/Sandfall/Binaries/Win64"
|
||||
ENGINE="Unreal Engine 5 (no DRM)"
|
||||
|
||||
# Fix method: EXTERNAL — we ship an upstream third-party fix (MIT) plus our own
|
||||
# 32:9-tuned config. We deliberately do NOT reimplement it; see README.md.
|
||||
METHOD="external"
|
||||
VENDOR_PAYLOAD="ClairObscurFix/Sandfall/Binaries/Win64" # contents copied into the exe dir
|
||||
EXTERNAL_CONFIG="ClairObscurFix.ini" # our tuned copy overrides upstream's
|
||||
EXTERNAL_FILES="ClairObscurFix.asi ClairObscurFix.ini dsound.dll"
|
||||
LOADER="dsound.dll"
|
||||
LAUNCH_OPTION='WINEDLLOVERRIDES="dsound=n,b" %command%'
|
||||
|
||||
PREFIX_INI=""
|
||||
STATUS="PARTIAL — reduced profile (Center HUD only); upstream v0.0.10 is archived and crashes with full features on current builds"
|
||||
+59
-13
@@ -105,32 +105,72 @@ 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" = "external" ]; then
|
||||
for f in $EXTERNAL_FILES; do [ -f "$GAMEDIR/$f" ] && rm -v "$GAMEDIR/$f"; done
|
||||
elif [ "$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" = "external" ]; then
|
||||
SRC="$HERE/vendor/$VENDOR_PAYLOAD"
|
||||
[ -d "$SRC" ] || { echo "[!] missing vendored payload: $SRC"; exit 1; }
|
||||
cp -v "$SRC"/* "$GAMEDIR/" 2>/dev/null || true
|
||||
# our tuned config wins over the upstream default
|
||||
if [ -n "${EXTERNAL_CONFIG:-}" ] && [ -f "$GAMES_DIR/$GAME/$EXTERNAL_CONFIG" ]; then
|
||||
cp -v "$GAMES_DIR/$GAME/$EXTERNAL_CONFIG" "$GAMEDIR/$EXTERNAL_CONFIG"
|
||||
fi
|
||||
elif [ "$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 <<EOF
|
||||
|
||||
[+] Installed: SUWSF.asi, SUWSF.ini, $LOADER
|
||||
[+] Installed ($METHOD): $(case "$METHOD" in
|
||||
bepinex) echo "BepInEx + $PLUGIN_DLL + $LOADER" ;;
|
||||
external) echo "$EXTERNAL_FILES" ;;
|
||||
*) echo "SUWSF.asi, SUWSF.ini, $LOADER" ;;
|
||||
esac)
|
||||
|
||||
NEXT (Proton) — REQUIRED, or the loader is ignored and the fix won't apply in gameplay.
|
||||
Steam > right-click ${TITLE:-$GAME} > Properties > Launch Options:
|
||||
@@ -138,6 +178,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
|
||||
<game>/BepInEx/config/*.cfg (edits apply live, ~2s, no restart)
|
||||
Plugin log (proves whether it applied):
|
||||
<game>/BepInEx/LogOutput.log
|
||||
|
||||
Troubleshooting:
|
||||
Different loader name: LOADER_NAME=version.dll ./install.sh $GAME
|
||||
Diagnose injection: add PROTON_LOG=1 to launch options, then
|
||||
|
||||
+40
-11
@@ -1,27 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build a drop-in release zip for one game.
|
||||
# ./tools/build_release.sh GundamBreaker4
|
||||
# Output: dist/<Game>_UltraWide_Fix.zip (attach to the GitHub Release)
|
||||
# Build a drop-in release zip for one game. Handles both fix methods.
|
||||
# ./tools/build_release.sh GundamBreaker4 (METHOD=suwsf)
|
||||
# ./tools/build_release.sh BurglinGnomes (METHOD=bepinex)
|
||||
# Output: dist/<Game>_UltraWide_Fix.zip
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$HERE"
|
||||
|
||||
GAME="${1:?usage: build_release.sh <GameName>}"
|
||||
GDIR="games/$GAME"
|
||||
V="vendor/SUWSF-x64"
|
||||
[ -d "$GDIR" ] || { echo "[!] no such game: $GDIR"; exit 1; }
|
||||
for f in "$V/SUWSF.asi" "$V/dsound.dll" "$V/LICENSE" "$GDIR/SUWSF.ini"; do
|
||||
[ -f "$f" ] || { echo "[!] missing $f"; exit 1; }
|
||||
done
|
||||
[ -f "$GDIR/game.conf" ] || { echo "[!] missing $GDIR/game.conf"; exit 1; }
|
||||
# shellcheck disable=SC1090
|
||||
. "$GDIR/game.conf"
|
||||
METHOD="${METHOD:-suwsf}"
|
||||
|
||||
OUT="dist/${GAME}_UltraWide_Fix"
|
||||
rm -rf "$OUT" "$OUT.zip"
|
||||
mkdir -p "$OUT"
|
||||
cp "$V/SUWSF.asi" "$V/dsound.dll" "$GDIR/SUWSF.ini" "$OUT/"
|
||||
cp "$V/LICENSE" "$OUT/SUWSF-LICENSE.txt"
|
||||
|
||||
if [ "$METHOD" = "external" ]; then
|
||||
SRC="vendor/$VENDOR_PAYLOAD"
|
||||
[ -d "$SRC" ] || { echo "[!] missing vendored payload: $SRC"; exit 1; }
|
||||
cp "$SRC"/* "$OUT/" 2>/dev/null || true
|
||||
[ -n "${EXTERNAL_CONFIG:-}" ] && [ -f "$GDIR/$EXTERNAL_CONFIG" ] && cp "$GDIR/$EXTERNAL_CONFIG" "$OUT/"
|
||||
# ship the upstream license alongside, as MIT requires
|
||||
UPLIC="vendor/$(dirname "$VENDOR_PAYLOAD" | cut -d/ -f1)/LICENSE"
|
||||
[ -f "$UPLIC" ] || UPLIC="vendor/${VENDOR_PAYLOAD%%/*}/LICENSE"
|
||||
[ -f "$UPLIC" ] && cp "$UPLIC" "$OUT/UPSTREAM-LICENSE.txt"
|
||||
echo "[+] Packaged vendored external fix + our config"
|
||||
elif [ "$METHOD" = "bepinex" ]; then
|
||||
BEPZIP=$(ls vendor/BepInEx_win_x64_*.zip 2>/dev/null | head -1)
|
||||
[ -f "$BEPZIP" ] || { echo "[!] missing BepInEx zip in vendor/"; exit 1; }
|
||||
[ -f "$GDIR/$PLUGIN_DLL" ] || { echo "[!] missing prebuilt plugin $GDIR/$PLUGIN_DLL"; exit 1; }
|
||||
command -v unzip >/dev/null || { echo "[!] unzip required"; exit 1; }
|
||||
unzip -o -q "$BEPZIP" -d "$OUT"
|
||||
mkdir -p "$OUT/BepInEx/plugins"
|
||||
cp "$GDIR/$PLUGIN_DLL" "$OUT/BepInEx/plugins/"
|
||||
echo "[+] Packaged BepInEx + $PLUGIN_DLL"
|
||||
else
|
||||
V="vendor/SUWSF-x64"
|
||||
for f in "$V/SUWSF.asi" "$V/dsound.dll" "$V/LICENSE" "$GDIR/SUWSF.ini"; do
|
||||
[ -f "$f" ] || { echo "[!] missing $f"; exit 1; }
|
||||
done
|
||||
cp "$V/SUWSF.asi" "$V/dsound.dll" "$GDIR/SUWSF.ini" "$OUT/"
|
||||
cp "$V/LICENSE" "$OUT/SUWSF-LICENSE.txt"
|
||||
echo "[+] Packaged SUWSF + patch ini"
|
||||
fi
|
||||
|
||||
cp "$GDIR/README.md" "$OUT/README.md" 2>/dev/null || true
|
||||
cp THIRD_PARTY.md "$OUT/" 2>/dev/null || true
|
||||
cp THIRD_PARTY.md LICENSE "$OUT/" 2>/dev/null || true
|
||||
|
||||
( cd dist && zip -r -q "${GAME}_UltraWide_Fix.zip" "${GAME}_UltraWide_Fix" )
|
||||
echo "[+] Built dist/${GAME}_UltraWide_Fix.zip"
|
||||
( cd dist && sha256sum "${GAME}_UltraWide_Fix.zip" )
|
||||
unzip -l "dist/${GAME}_UltraWide_Fix.zip"
|
||||
|
||||
+44
-22
@@ -12,24 +12,45 @@ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"; cd "$HERE"
|
||||
HOST="${GITEA_HOST:-https://git.lazypugs.com}"
|
||||
OWNER="${GITEA_OWNER:-ckoch}"
|
||||
REPO="${GITEA_REPO:-UltraWidePatches}"
|
||||
GAME="${GAME:-GundamBreaker4}"
|
||||
TAG="${TAG:-v1.0.0}"
|
||||
NOTES="${NOTES:-RELEASE_NOTES.md}"
|
||||
# GAME may be a space-separated list; empty/"all" = every game in games/
|
||||
GAME="${GAME:-all}"
|
||||
if [ "$GAME" = "all" ]; then
|
||||
GAME=$(for d in games/*/; do [ -f "$d/game.conf" ] && basename "$d"; done | tr '\n' ' ')
|
||||
fi
|
||||
TAG="${TAG:?set TAG explicitly, e.g. TAG=v1.2.0 (see CHANGELOG.md)}"
|
||||
# Single game -> its own README; multiple -> the collection README.
|
||||
if [ "$(echo "$GAME" | wc -w)" -eq 1 ] && [ -f "games/$GAME/README.md" ]; then
|
||||
NOTES="${NOTES:-games/$GAME/README.md}"
|
||||
else
|
||||
NOTES="${NOTES:-README.md}"
|
||||
fi
|
||||
[ -f "$NOTES" ] || NOTES="README.md"
|
||||
API="$HOST/api/v1"
|
||||
|
||||
[ -n "${GITEA_TOKEN:-}" ] || { echo "[!] GITEA_TOKEN is not set. Create one at $HOST/user/settings/applications (scope: write:repository)"; exit 1; }
|
||||
AUTH=(-H "Authorization: token $GITEA_TOKEN")
|
||||
ZIPNAME="${GAME}_UltraWide_Fix.zip"
|
||||
echo "[+] Games in this release: $GAME"
|
||||
|
||||
# 1. build the artifact
|
||||
bash tools/build_release.sh "$GAME" >/dev/null
|
||||
ZIP="dist/$ZIPNAME"
|
||||
[ -f "$ZIP" ] || { echo "[!] build produced no $ZIP"; exit 1; }
|
||||
echo "[+] Built $ZIP"
|
||||
# 1. build every artifact -- do NOT swallow output; a failure here used to exit
|
||||
# the whole script silently because stdout went to /dev/null under `set -e`.
|
||||
ZIPS=""
|
||||
for g in $GAME; do
|
||||
if ! bash tools/build_release.sh "$g"; then
|
||||
echo "[!] build_release.sh failed for game '$g' (see above)"; exit 1
|
||||
fi
|
||||
z="dist/${g}_UltraWide_Fix.zip"
|
||||
[ -f "$z" ] || { echo "[!] build produced no $z"; exit 1; }
|
||||
ZIPS="$ZIPS $z"
|
||||
done
|
||||
echo "[+] Built:$ZIPS"
|
||||
|
||||
# 2. push code + tag
|
||||
git push origin main
|
||||
git push origin "$TAG" 2>/dev/null || echo "[i] tag $TAG already on remote"
|
||||
if ! git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
|
||||
git tag -a "$TAG" -m "$TAG"
|
||||
echo "[+] Created tag $TAG at $(git rev-parse --short HEAD)"
|
||||
fi
|
||||
git push origin "$TAG" 2>&1 | tail -1
|
||||
|
||||
# 3. create the release (or reuse if it already exists)
|
||||
REL_ID=$(curl -sf "${AUTH[@]}" "$API/repos/$OWNER/$REPO/releases/tags/$TAG" 2>/dev/null \
|
||||
@@ -43,23 +64,24 @@ if [ -z "$REL_ID" ]; then
|
||||
| python3 -c "import sys,json;print(json.load(sys.stdin)['id'])")
|
||||
echo "[+] Created release $TAG (id=$REL_ID)"
|
||||
else
|
||||
echo "[i] Release $TAG exists (id=$REL_ID); replacing asset"
|
||||
echo "[i] Release $TAG exists (id=$REL_ID); replacing assets"
|
||||
fi
|
||||
|
||||
# 4. upload each zip, replacing any same-named asset already attached
|
||||
for z in $ZIPS; do
|
||||
n=$(basename "$z")
|
||||
OLD=$(curl -sf "${AUTH[@]}" "$API/repos/$OWNER/$REPO/releases/$REL_ID" \
|
||||
| ZIPNAME="$ZIPNAME" python3 -c "
|
||||
| ZIPNAME="$n" python3 -c "
|
||||
import sys, json, os
|
||||
d = json.load(sys.stdin)
|
||||
want = os.environ['ZIPNAME']
|
||||
print(next((str(a['id']) for a in d.get('assets', []) if a['name'] == want), ''))" 2>/dev/null || true)
|
||||
if [ -n "$OLD" ]; then
|
||||
curl -sf -X DELETE "${AUTH[@]}" "$API/repos/$OWNER/$REPO/releases/$REL_ID/assets/$OLD" >/dev/null \
|
||||
&& echo "[i] removed previous asset"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. upload the zip
|
||||
curl -sf "${AUTH[@]}" -F "attachment=@$ZIP" \
|
||||
"$API/repos/$OWNER/$REPO/releases/$REL_ID/assets?name=$ZIPNAME" >/dev/null
|
||||
echo "[+] Uploaded $ZIPNAME"
|
||||
[ -n "$OLD" ] && curl -sf -X DELETE "${AUTH[@]}" \
|
||||
"$API/repos/$OWNER/$REPO/releases/$REL_ID/assets/$OLD" >/dev/null && echo "[i] replaced $n"
|
||||
curl -sf "${AUTH[@]}" -F "attachment=@$z" \
|
||||
"$API/repos/$OWNER/$REPO/releases/$REL_ID/assets?name=$n" >/dev/null
|
||||
echo "[+] Uploaded $n"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "[+] Done: $HOST/$OWNER/$REPO/releases/tag/$TAG"
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// UnityExplorer C# console diagnostic — paste into the C# Console tab, hit Run.
|
||||
// Run once at ultrawide and once at 16:9, then compare.
|
||||
//
|
||||
// NOTE: Burglin' Gnomes uses an ORTHOGRAPHIC camera, so fieldOfView is meaningless.
|
||||
// The governing value is orthographicSize (half the visible world HEIGHT):
|
||||
// visible height = 2 * orthographicSize
|
||||
// visible width = visible height * aspect
|
||||
// If orthographicSize is CONSTANT across resolutions -> already Hor+ (correct).
|
||||
// If it SHRINKS at wider aspect -> the game holds world-width constant (Vert- lock).
|
||||
var sb = new System.Text.StringBuilder();
|
||||
|
||||
sb.AppendLine($"SCREEN {UnityEngine.Screen.width}x{UnityEngine.Screen.height} " +
|
||||
$"aspect={(float)UnityEngine.Screen.width / UnityEngine.Screen.height:F4} " +
|
||||
$"full={UnityEngine.Screen.fullScreenMode}");
|
||||
|
||||
foreach (var c in UnityEngine.Object.FindObjectsOfType<UnityEngine.Camera>())
|
||||
{
|
||||
if (c.orthographic)
|
||||
sb.AppendLine($"CAM {c.name} ORTHO size={c.orthographicSize:F4} aspect={c.aspect:F4} " +
|
||||
$"=> visibleW={2f * c.orthographicSize * c.aspect:F3} visibleH={2f * c.orthographicSize:F3} " +
|
||||
$"depth={c.depth} enabled={c.enabled}");
|
||||
else
|
||||
sb.AppendLine($"CAM {c.name} PERSP fov={c.fieldOfView:F2} aspect={c.aspect:F4} " +
|
||||
$"phys={c.usePhysicalProperties} gate={c.gateFit} depth={c.depth} enabled={c.enabled}");
|
||||
}
|
||||
|
||||
// --- Cinemachine (any version) via reflection: may drive ortho size each frame ---
|
||||
var bf = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance;
|
||||
foreach (var mb in UnityEngine.Object.FindObjectsOfType<UnityEngine.MonoBehaviour>())
|
||||
{
|
||||
var t = mb.GetType();
|
||||
if (t.FullName == null || t.FullName.IndexOf("Cinemachine", System.StringComparison.OrdinalIgnoreCase) < 0)
|
||||
continue;
|
||||
object lens = null;
|
||||
foreach (var name in new[] { "Lens", "m_Lens" })
|
||||
{
|
||||
var p = t.GetProperty(name, bf); if (p != null) { lens = p.GetValue(mb); break; }
|
||||
var f = t.GetField(name, bf); if (f != null) { lens = f.GetValue(mb); break; }
|
||||
}
|
||||
if (lens != null)
|
||||
{
|
||||
var lt = lens.GetType();
|
||||
object osize = null, fov = null;
|
||||
foreach (var n in new[] { "OrthographicSize", "m_OrthographicSize" })
|
||||
{ var f = lt.GetField(n, bf); if (f != null) { osize = f.GetValue(lens); break; } }
|
||||
foreach (var n in new[] { "FieldOfView", "m_FieldOfView" })
|
||||
{ var f = lt.GetField(n, bf); if (f != null) { fov = f.GetValue(lens); break; } }
|
||||
sb.AppendLine($"CM {t.Name} '{mb.name}' lensOrthoSize={osize} lensFOV={fov} enabled={mb.enabled}");
|
||||
}
|
||||
else sb.AppendLine($"CM {t.Name} '{mb.name}' (no lens) enabled={mb.enabled}");
|
||||
}
|
||||
|
||||
// --- uGUI canvas scalers: match=0 scales by WIDTH (bad at ultrawide), 1 = by HEIGHT ---
|
||||
foreach (var cs in UnityEngine.Object.FindObjectsOfType<UnityEngine.UI.CanvasScaler>())
|
||||
sb.AppendLine($"UI {cs.name} mode={cs.uiScaleMode} ref={cs.referenceResolution} " +
|
||||
$"match={cs.matchWidthOrHeight} screenMatch={cs.screenMatchMode} scaleFactor={cs.scaleFactor}");
|
||||
|
||||
UnityExplorer.ExplorerCore.Log(sb.ToString());
|
||||
@@ -0,0 +1,36 @@
|
||||
// LIVE FIX TEST — paste into UnityExplorer's C# Console while running at ultrawide.
|
||||
// Applies both candidate fixes immediately so you can see the result on screen.
|
||||
// Nothing is persisted: relaunching the game reverts everything.
|
||||
|
||||
var log = new System.Text.StringBuilder();
|
||||
|
||||
// ---- FIX 1: HUD scaling ----------------------------------------------------
|
||||
// CanvasScaler.matchWidthOrHeight: 0 = scale by WIDTH, 1 = scale by HEIGHT.
|
||||
// The game ships 0.453 (width-biased), which balloons the HUD at 5120px wide.
|
||||
// Matching HEIGHT keeps the HUD the same physical size as it is at 16:9.
|
||||
foreach (var cs in UnityEngine.Object.FindObjectsOfType<UnityEngine.UI.CanvasScaler>())
|
||||
{
|
||||
log.AppendLine($"UI {cs.name}: match {cs.matchWidthOrHeight} -> 1.0");
|
||||
cs.matchWidthOrHeight = 1f;
|
||||
}
|
||||
|
||||
// ---- FIX 2: orthographic view width ---------------------------------------
|
||||
// For an ortho camera, visible height = 2*orthographicSize and width follows
|
||||
// aspect. If the game shrank orthographicSize at ultrawide, restoring the 16:9
|
||||
// value gives back the vertical view (true Hor+).
|
||||
// Set TARGET_ORTHO to the size you measured at 1920x1080; leave null to only report.
|
||||
float? TARGET_ORTHO = null; // e.g. 5.5f
|
||||
|
||||
foreach (var c in UnityEngine.Object.FindObjectsOfType<UnityEngine.Camera>())
|
||||
{
|
||||
if (!c.orthographic) continue;
|
||||
log.AppendLine($"CAM {c.name}: orthoSize={c.orthographicSize:F4} aspect={c.aspect:F4} " +
|
||||
$"visibleW={2f * c.orthographicSize * c.aspect:F3} visibleH={2f * c.orthographicSize:F3}");
|
||||
if (TARGET_ORTHO.HasValue)
|
||||
{
|
||||
c.orthographicSize = TARGET_ORTHO.Value;
|
||||
log.AppendLine($" -> forced orthoSize={TARGET_ORTHO.Value}");
|
||||
}
|
||||
}
|
||||
|
||||
UnityExplorer.ExplorerCore.Log(log.ToString());
|
||||
@@ -0,0 +1,82 @@
|
||||
// LIVE UI TUNER — paste into UnityExplorer's C# Console. Edit knobs, Run, look, repeat.
|
||||
// Nothing persists across a relaunch.
|
||||
//
|
||||
// TWO canvas families, each needing a DIFFERENT lever:
|
||||
//
|
||||
// 1. ScreenSpaceOverlay + ScaleWithScreenSize ("Canvas", "Canvas in front")
|
||||
// The HUD. Lever = matchWidthOrHeight + referenceResolution.
|
||||
// KEEP MATCH AT 1.0 — lowering it re-introduces width-based scaling and
|
||||
// undoes the shrink (match .8 + ref 1350 => 1.281, vs match 1 => 1.067).
|
||||
//
|
||||
// 2. WorldSpace canvases ("NamePlate", etc.)
|
||||
// In-world labels. CanvasScaler.scaleFactor DOES NOT WORK here: for a
|
||||
// WorldSpace canvas, CanvasScaler.HandleWorldCanvas() overwrites
|
||||
// scaleFactor with dynamicPixelsPerUnit every frame. The real lever is
|
||||
// the canvas transform's localScale.
|
||||
//
|
||||
// NOTE: Canvas.scaleFactor is recomputed in CanvasScaler.Update(), so reading it
|
||||
// right after writing returns LAST frame's value. We print the mathematically
|
||||
// expected scale instead, and echo BEFORE state (which is accurate) each run.
|
||||
|
||||
// ---- KNOBS -----------------------------------------------------------------
|
||||
float MATCH = 1.0f; // HUD. Leave at 1.0.
|
||||
float HUD_SCALE = 0.85f; // HUD. <1 = smaller HUD + more layout room.
|
||||
float WORLD_SCALE = 0f; // In-world labels. 0 = don't touch.
|
||||
// Absolute multiplier vs the BASELINE printed below.
|
||||
// Run once with 0 to read baseline, then e.g. 0.8f.
|
||||
bool LIST_ONLY = false; // true = report only, change nothing
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
var log = new System.Text.StringBuilder();
|
||||
log.AppendLine($"KNOBS match={MATCH} hudScale={HUD_SCALE} worldScale={WORLD_SCALE} listOnly={LIST_ONLY}");
|
||||
log.AppendLine($"SCREEN {UnityEngine.Screen.width}x{UnityEngine.Screen.height} " +
|
||||
$"aspect={(float)UnityEngine.Screen.width / UnityEngine.Screen.height:F4}");
|
||||
|
||||
foreach (var cs in UnityEngine.Object.FindObjectsOfType<UnityEngine.UI.CanvasScaler>())
|
||||
{
|
||||
var cv = cs.GetComponent<UnityEngine.Canvas>();
|
||||
if (cv == null) continue;
|
||||
|
||||
if (cv.renderMode == UnityEngine.RenderMode.WorldSpace)
|
||||
{
|
||||
var ls = cv.transform.localScale;
|
||||
log.AppendLine($"WORLD {cs.name}: localScale={ls} (dynPPU={cs.dynamicPixelsPerUnit})");
|
||||
if (!LIST_ONLY && WORLD_SCALE > 0f)
|
||||
{
|
||||
// Store the pristine scale on first touch so repeated runs don't compound.
|
||||
var marker = cv.transform.Find("__uwp_baseline");
|
||||
float bx;
|
||||
if (marker == null)
|
||||
{
|
||||
var go = new UnityEngine.GameObject("__uwp_baseline");
|
||||
go.transform.SetParent(cv.transform, false);
|
||||
go.transform.localScale = ls; // stash baseline here
|
||||
bx = ls.x;
|
||||
}
|
||||
else bx = marker.localScale.x;
|
||||
|
||||
cv.transform.localScale = new UnityEngine.Vector3(bx, bx, bx) * WORLD_SCALE;
|
||||
log.AppendLine($" APPLIED localScale baseline={bx:F5} -> {cv.transform.localScale}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
log.AppendLine($"BEFORE {cs.name} [{cs.uiScaleMode}/{cv.renderMode}] ref={cs.referenceResolution} " +
|
||||
$"match={cs.matchWidthOrHeight}");
|
||||
|
||||
if (!LIST_ONLY && cs.uiScaleMode == UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize)
|
||||
{
|
||||
cs.matchWidthOrHeight = MATCH;
|
||||
if (HUD_SCALE > 0f && System.Math.Abs(HUD_SCALE - 1f) > 0.0001f)
|
||||
cs.referenceResolution = new UnityEngine.Vector2(cs.referenceResolution.x, 1080f / HUD_SCALE);
|
||||
|
||||
// expected scale (accurate NOW; Canvas.scaleFactor lags by a frame)
|
||||
var r = cs.referenceResolution;
|
||||
double exp = System.Math.Pow(UnityEngine.Screen.width / r.x, 1.0 - MATCH) *
|
||||
System.Math.Pow(UnityEngine.Screen.height / r.y, MATCH);
|
||||
log.AppendLine($" APPLIED ref={r} match={MATCH} => expectedScale={exp:F4} " +
|
||||
$"logicalCanvas={UnityEngine.Screen.width / exp:F0}x{UnityEngine.Screen.height / exp:F0}");
|
||||
}
|
||||
}
|
||||
|
||||
UnityExplorer.ExplorerCore.Log(log.ToString());
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Lyall
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Binary file not shown.
@@ -0,0 +1,49 @@
|
||||
;;;;;;;;;; General ;;;;;;;;;;
|
||||
|
||||
[Developer Console]
|
||||
; Set "Enabled" to true to enable the console.
|
||||
; By default you can access it with the key "Tilde".
|
||||
Enabled = false
|
||||
|
||||
[Skip Intro Logos]
|
||||
; Skips splash screen logos, epilepsy warning and autosave warning.
|
||||
Enabled = false
|
||||
|
||||
[Uncap Cutscene FPS]
|
||||
; Removes 30fps cap in cutscenes.
|
||||
Enabled = true
|
||||
|
||||
[Adjust Resolution Checks]
|
||||
; Adjusts resolution checks to use the monitor's maximum supported resolution, rather than the "native" resolution.
|
||||
Enabled = true
|
||||
|
||||
[Background Audio]
|
||||
; Set "Enabled" to true to allow audio to continue playing when the game window has lost focus.
|
||||
Enabled = false
|
||||
|
||||
;;;;;;;;;; Ultrawide/Narrower ;;;;;;;;;;
|
||||
|
||||
[Cutscene Letterboxing]
|
||||
; Set "Enabled" to true to re-enable pillarboxing/letterboxing in cutscenes.
|
||||
Enabled = false
|
||||
|
||||
[Fix Movies]
|
||||
; Crops letterboxing from pre-rendered movies at ultrawide resolutions.
|
||||
Enabled = true
|
||||
|
||||
[Center HUD]
|
||||
; Set "Enabled" to true to center certain parts of the HUD to 16:9 at ultrawide resolutions.
|
||||
Enabled = false
|
||||
; Adjust "AspectRatio" to set the aspect ratio of the HUD. i.e 1.77778 for 16:9, 2.33333 for 21:9 etc
|
||||
AspectRatio = 1.77778
|
||||
|
||||
;;;;;;;;;; Graphics ;;;;;;;;;;
|
||||
|
||||
[Disable Subtitle Blur]
|
||||
; Set "Enabled" to true to disable the background blur on cutscene subtitle lines.
|
||||
Enabled = false
|
||||
|
||||
[Sharpening]
|
||||
; Adjust "Strength" to control the intensity of the forced post-process sharpening.
|
||||
; Valid range: 0 to 2.0 Game Default = 1.0
|
||||
Strength = 0
|
||||
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 ThirteenAG
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user