8 Commits
Author SHA1 Message Date
ckochandClaude Opus 4.8 83748d6422 Remove Clair Obscur: Expedition 33 from the collection
The game supports ultrawide natively, so there is nothing for this project to
fix. Deletes games/Expedition33 and all references (README table, THIRD_PARTY,
the never-published v1.2.0 changelog section). A one-line changelog note records
why it was dropped, and the METHOD=native tooling capability stays for any
future works-out-of-the-box title.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:46:37 -04:00
ckochandClaude Opus 4.8 97d3c3e72a Expedition 33: drop vendored fix, mark native (fixes crash)
After a reboot E33 kept crashing. The ClairObscurFix.log showed why the earlier
'reduced profile' never helped: config flags gate behaviour INSIDE the fix's
hooks, but the .asi installs all its hooks (Level Load, Widgets, cutscene,
PostProcess) regardless. So the crash surface was never reduced - only removing
the fix stops it.

The game added official ultrawide support in patch 1.2.3 and renders correctly
at 5120x1440 natively, so the archived fix was pure liability. Removed it
entirely and marked the entry native.

- New METHOD=native: install.sh no-ops with a message, build/publish skip it.
- Deleted vendor/ClairObscurFix and the tuned ini (unused, crash-causing).
- README/THIRD_PARTY/CHANGELOG updated; ClairObscurFix recorded as historical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 19:10:50 -04:00
ckochandClaude Opus 4.8 2f781f95c9 Add ONE PIECE ODYSSEY (UE4 + SteamStub, same profile as GB4)
Recon: UE4, SteamStub-wrapped (.bind present, .text entropy 8.00), no native
ultrawide, and the community workaround is raising FOV at 32:9 - the Vert-
signature. That is the Gundam Breaker 4 scenario exactly, so it reuses the
proven SUWSF method and UE4-generic patterns.

Patterns cannot be verified statically because the exe is encrypted; marked
unverified until a runtime check confirms the matches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:53:54 -04:00
ckochandClaude Opus 4.8 1176df8f20 Add CHANGELOG; require an explicit release TAG
Adding a game is a minor bump, so the collection is at v1.2.0: v1.0.0 shipped
GB4, Burglin' Gnomes was a minor bump that was never cut, and Expedition 33 is
the second. Skipping the unreleased 1.1.0 reflects the real history better than
collapsing two games' worth of additions into one bump.

publish.sh no longer defaults TAG to v1.0.0, which could silently re-cut the
existing release; it now requires TAG explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:23:23 -04:00
ckochandClaude Opus 4.8 15d0169f56 publish.sh: one release for the whole collection
GAME now accepts a space-separated list and defaults to 'all', building and
attaching every game's zip to a single release instead of one game per tag -
which matches the repo being a collection. Release body is the game's own
README for a single game, or the collection README for several.

Asset upload replaces same-named assets, so re-running is idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:16:54 -04:00
ckochandClaude Opus 4.8 6f24502956 Expedition 33: record version drift honestly, ship reduced profile
Running ClairObscurFix v0.0.10 with upstream defaults crashed on save load.
Removing it entirely let the game load normally, isolating the fix as the cause
rather than Proton or the year-old save. The fix is archived and predates the
current build; two pattern scans already failed outright, and the crash
coincides with its Level Load hook.

Ship a reduced profile with only Center HUD enabled - the one option actually
needed at 32:9 - and mark the game PARTIAL rather than implying full support.
Adds a revalidation checklist since vendored archived fixes decay as the game
patches, plus a note that the game has no DRM, so a dedicated HUD-centering
patch is a viable replacement if Center HUD eventually breaks too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:15:12 -04:00
ckochandClaude Opus 4.8 0833cf8495 Add Clair Obscur: Expedition 33 via new METHOD=external
Expedition 33 is UE5 with no DRM, but a mature MIT-licensed fix already exists:
ClairObscurFix by Lyall. Rather than reimplement it, install.sh gains a third
method that vendors a third-party fix unmodified and layers our own tuned
config on top, with the upstream license shipped in the release zip as MIT
requires. Upstream is archived, so vendoring also keeps it available.

Our only change vs upstream defaults is [Center HUD] Enabled = true, since at
5120px wide the edge-anchored HUD elements end up absurdly far apart.

Install/uninstall round-trip verified against a scratch game dir, including
that our tuned ini overrides the vendored default.

Credit to Lyall is recorded in THIRD_PARTY.md, the game README, and the repo
README's method table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 16:58:05 -04:00
ckochandClaude Opus 4.8 efaab8bd32 Fix release pipeline: bepinex packaging + no silent failures
'GAME=BurglinGnomes ./tools/publish.sh' exited with zero output. Two causes:

1. build_release.sh was SUWSF-only and hard-failed on a missing SUWSF.ini for
   BepInEx games. It now reads METHOD from game.conf and packages BepInEx plus
   the prebuilt plugin instead.
2. publish.sh ran the build with stdout to /dev/null, so that error was
   invisible and  killed the script silently. Build output is now shown
   and failure reported explicitly.

Also: publish.sh creates the tag locally when missing (v1.1.0 never existed, so
the tag push would have failed too), and defaults the release body to the
game's own README so per-game notes are always accurate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 16:40:41 -04:00
9 changed files with 334 additions and 37 deletions
+51
View File
@@ -0,0 +1,51 @@
# Changelog
## Unreleased
- **Dropped Clair Obscur: Expedition 33.** Evaluated and removed from the
collection: the game supports ultrawide natively (official support since patch
1.2.3), so there is nothing for this project to fix. The archived third-party
fix we briefly vendored crashed on current builds and is gone too. A
`METHOD=native` capability remains in the tooling for any future
works-out-of-the-box title.
- **New game: ONE PIECE ODYSSEY** (UE4 + SteamStub). SUWSF loads, but the
GB4 patterns don't match this build (FOV pattern 0 matches; pillarbox pattern
false-matched). Needs runtime-derived patterns. ⚠️ in progress.
Versioning applies to the collection as a whole: **adding a game is a minor bump**,
fixes/tuning to existing entries are patch bumps.
## v1.1.0 — Burglin' Gnomes *(unreleased)*
- **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.
+10 -1
View File
@@ -6,6 +6,7 @@ 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.
@@ -15,6 +16,7 @@ Most force **Hor+ FOV** (a wider screen shows you *more* rather than zooming in)
|---|---|---|---|
| [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 |
| [ONE PIECE ODYSSEY](games/OnePieceOdyssey/) | Unreal Engine 4 | SUWSF | ⚠️ New — unverified |
## Install
@@ -40,6 +42,7 @@ Config-file tweaks (e.g. UE4's `AspectRatioAxisConstraint=MaintainYFOV`) typical
|---|---|
| `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 |
@@ -54,10 +57,16 @@ Config-file tweaks (e.g. UE4's `AspectRatioAxisConstraint=MaintainYFOV`) typical
## Adding a game
1. `mkdir games/<GameName>` with a `game.conf` (copy GB4's as a template) and a `SUWSF.ini`.
2. Set `METHOD=` in `game.conf` to `suwsf` or `bepinex`.
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
MIT — see [LICENSE](LICENSE). Bundled third-party components (SUWSF, Ultimate ASI Loader) are MIT and credited in [THIRD_PARTY.md](THIRD_PARTY.md). You must own the games; nothing here modifies or circumvents copy protection.
+1
View File
@@ -32,6 +32,7 @@ them; all runtime patching is performed by SUWSF.
- Used only for runtime inspection while developing a fix; it is NOT part of any
release package and is not required by end users.
## Patch patterns
The `AspectRatioAxisConstraint` (HOR+) and `ConstrainAspectRatio` (pillarbox)
byte patterns in `games/*/SUWSF.ini` are adapted from the UE4 fixes in
+65
View File
@@ -0,0 +1,65 @@
# ONE PIECE ODYSSEY — UltraWide Fix
⚠️ **New — patterns not yet verified at runtime.**
- **Engine:** Unreal Engine 4
- **Steam AppID:** 814000
- **Executable:** `odyssey-Win64-Shipping.exe` (**SteamStub-encrypted**)
- **Method:** SUWSF runtime memory patch
## Why this mirrors Gundam Breaker 4
Same engine, same DRM, same symptom — the game has no native ultrawide support and
the community workaround is *raising FOV* at 32:9, which is the Vert- signature:
the camera holds horizontal FOV constant, so a wider display crops vertically
instead of showing more.
Because the exe is SteamStub-wrapped (`.bind` section, `.text` entropy 8.00), it is
encrypted on disk and cannot be patched statically. SUWSF patches the decrypted
image in memory at launch, which is also why the fix survives into gameplay.
The patterns are the UE4-generic ones proven on Gundam Breaker 4.
## Install (Linux / Proton)
```bash
./install.sh OnePieceOdyssey
```
Then **required** — Steam → Properties → Launch Options:
```
WINEDLLOVERRIDES="dsound=n,b" %command%
```
Set **5120×1440** fullscreen in-game.
## Patches
| Patch | Effect |
|---|---|
| `AspectRatioAxisConstraint` | Forces `MaintainYFOV` → Hor+ FOV. **Do not use at 16:9 or narrower.** |
| `ConstrainAspectRatio` | NOPs the pillarbox/letterbox clamp |
| `HardcodedAspect_16by9_*` | Optional fallbacks, disabled by default |
## Verifying / troubleshooting
Add `PROTON_LOG=1` to the launch options, then:
```bash
./tools/read_protonlog.sh 814000
```
SUWSF reports each pattern as `Found patch … N matches` or `No pattern found, skipping`.
If `AspectRatioAxisConstraint` misses, derive the exact bytes from a runtime dump:
```bash
python3 tools/dump_decrypted.py odyssey-Win64-Shipping.exe # while the game runs
python3 tools/aob.py <dump> games/OnePieceOdyssey/SUWSF.ini
```
## Open questions
- [ ] Do the GB4 patterns match this build?
- [ ] Is the HUD sane at 32:9, or stretched/misplaced?
- [ ] Any pillarboxing left in cutscenes?
+71
View File
@@ -0,0 +1,71 @@
[UserSettings]
; ==========================================================================
; ONE PIECE ODYSSEY - UltraWide Fix (SUWSF patch)
; Target display: Samsung 49" Odyssey G93SC (5120x1440, 32:9)
; Engine: Unreal Engine 4 | Exe is SteamStub-encrypted -> runtime patch
; ==========================================================================
; Your screen resolution used in patch calculations. "auto" reads primary
; display; set explicitly if auto misreads under Proton/DPI scaling.
Resolution="5120x1440"
; Globally enable or disable all patches (default: true)
Enabled=true
; --------------------------------------------------------------------------
; WARNING: PATCHES BELOW. These are CANDIDATE UE4 patterns adapted from
; known-good UE4 fixes (Trepang2 / Lies of P). Because GB4's .text is
; SteamStub-encrypted on disk we cannot verify the exact bytes statically;
; SUWSF scans the DECRYPTED image in memory at runtime, so if GB4's UE4.27
; codegen matches these generic patterns they apply directly.
;
; If a patch reports 0 matches (see SUWSF log / no visual change), run
; tools/dump_decrypted.sh while the game is running and send me the dump so
; I can derive GB4's exact pattern. Each patch is independent - a miss on one
; does not stop the others.
; --------------------------------------------------------------------------
; [1] HOR+ FOV -- the primary fix.
; Forces ULocalPlayer AspectRatioAxisConstraint to 0 (AspectRatio_MaintainYFOV).
; This makes the camera keep VERTICAL FOV constant and expand horizontally
; (Hor+), removing the zoomed-in feel at 32:9. Do NOT use at 16:9 or narrower.
; Value BA 00 00 00 00 = "mov edx, 0" ; 90 90 90 = pad nops.
[Patch:AspectRatioAxisConstraint]
Enabled=true
Pattern="0F ?? ?? ?? 0F ?? ?? ?? ?? ?? 41 ?? ?? ?? ?? ?? 00 00 48 ?? ?? ?? ?? 00 00 4C ?? ??"
Offset=10
Value="BA 00 00 00 00 90 90 90"
ValueType="byte"
Match="1"
Module="auto"
; [2] Disable pillarboxing/letterboxing (bConstrainAspectRatio = 0).
; Prevents the game clamping the render to 16:9 with black side bars.
[Patch:ConstrainAspectRatio]
Enabled=true
Pattern="33 ?? ?? 83 ?? 02 31 ?? ?? 0F ?? ?? ?? ?? 00 00"
Offset=0
Value="90 90 90 90 90 90"
ValueType="byte"
Match="1"
Module="auto"
; [3] OPTIONAL fallback: some UE builds hardcode 16:9 (1.777778) as a float
; (39 8E E3 3F) or (3B 8E E3 3F) and clamp to it. Disabled by default because
; it can double-apply with patch [1]. Enable ONLY if [1] misses and you still
; see a squeezed/zoomed image. Writes your true aspect ratio in its place.
[Patch:HardcodedAspect_16by9_a]
Enabled=false
Pattern="39 8E E3 3F"
Offset=0
Value="aspectratio"
ValueType="float"
Match="all"
Module="auto"
[Patch:HardcodedAspect_16by9_b]
Enabled=false
Pattern="3B 8E E3 3F"
Offset=0
Value="aspectratio"
ValueType="float"
Match="all"
Module="auto"
+20
View File
@@ -0,0 +1,20 @@
# ONE PIECE ODYSSEY — metadata for ../../install.sh
TITLE="ONE PIECE ODYSSEY"
APPID=814000
EXE="odyssey-Win64-Shipping.exe"
COMMON_SUBDIR="ONE PIECE ODYSSEY/odyssey/Binaries/Win64"
ENGINE="Unreal Engine 4 (SteamStub DRM — runtime patching required)"
METHOD="suwsf"
LOADER="dsound.dll"
LAUNCH_OPTION='WINEDLLOVERRIDES="dsound=n,b" %command%'
# UE4 config-level partial win: fixes menus immediately; SUWSF carries gameplay.
PREFIX_INI="drive_c/users/steamuser/AppData/Local/odyssey/Saved/Config/WindowsNoEditor/Engine.ini"
read -r -d '' INI_BLOCK <<'BLOCK' || true
[/Script/Engine.LocalPlayer]
AspectRatioAxisConstraint=AspectRatio_MaintainYFOV
BLOCK
STATUS="NEW — UE4+SteamStub like GB4; patterns unverified at runtime"
+23 -3
View File
@@ -107,9 +107,17 @@ remove_ini() {
METHOD="${METHOD:-suwsf}"
if [ "$METHOD" = "native" ]; then
echo "[i] ${TITLE:-$GAME} supports ultrawide natively — no fix to install or remove."
echo " ${STATUS:-}"
exit 0
fi
if [ "$ACTION" = "uninstall" ]; then
echo "[+] Uninstalling..."
if [ "$METHOD" = "bepinex" ]; then
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
@@ -125,7 +133,15 @@ if [ "$ACTION" = "uninstall" ]; then
fi
# --- install ------------------------------------------------------------------
if [ "$METHOD" = "bepinex" ]; then
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; }
@@ -156,7 +172,11 @@ apply_ini
cat <<EOF
[+] Installed ($METHOD): $([ "$METHOD" = "bepinex" ] && echo "BepInEx + $PLUGIN_DLL + $LOADER" || echo "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:
+44 -11
View File
@@ -1,27 +1,60 @@
#!/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}"
if [ "${METHOD:-suwsf}" = "native" ]; then
echo "[i] $GAME is native (no fix) — nothing to package."; exit 0
fi
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"
+49 -22
View File
@@ -12,24 +12,50 @@ 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" ] || continue
m=$(. "$d/game.conf" >/dev/null 2>&1; echo "${METHOD:-suwsf}")
[ "$m" = "native" ] && continue # native entries have no release zip
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 +69,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"