Ship Switch releases as an SD-ready zip only.

Players extract one zip at the microSD root for install and update; saves under pokemon-love2d/ survive merge. Drop the bare .nro from GitHub Release assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Andrew Quenehen
2026-08-03 09:04:25 -03:00
parent eeb8c89d3d
commit d7581dbece
11 changed files with 328 additions and 61 deletions
+7 -12
View File
@@ -1,7 +1,7 @@
name: Release
# Builds the macOS, Windows, and Linux desktop apps, an Android APK, an iOS
# IPA, a Nintendo Switch fused NRO (experimental), and the Anbernic RG34XXSP
# IPA, a Nintendo Switch SD-ready zip (experimental), and the Anbernic RG34XXSP
# (Stock OS 64-bit MOD / PortMaster) port on the self-hosted Mac runner, and
# publishes them as a GitHub Release.
#
@@ -270,13 +270,11 @@ jobs:
[ -f "$ipa" ] || { echo "::error::$ipa not found (expected from scripts/build_ios.sh --device)"; exit 1; }
cp "$ipa" "$outdir/gen1recomp-${v}-ios.ipa"
nro="dist/switch/gen1recomp-${v}-switch.nro"
[ -f "$nro" ] || { echo "::error::$nro not found (expected from scripts/build_switch.sh --fused)"; exit 1; }
cp "$nro" "$outdir/gen1recomp-${v}-switch.nro"
# Sidecar written by build_fused.sh when the fused NRO succeeds.
if [ -f "${nro}.sha256" ]; then
cp "${nro}.sha256" "$outdir/gen1recomp-${v}-switch.nro.sha256"
fi
swzip="dist/switch/gen1recomp-${v}-switch.zip"
[ -f "$swzip" ] || { echo "::error::$swzip not found (expected from scripts/build_switch.sh --fused → pack_sd_zip.sh)"; exit 1; }
cp "$swzip" "$outdir/gen1recomp-${v}-switch.zip"
# Local fused .nro stays under dist/switch/ for PR CI / debug; release
# publishes the SD-ready zip only.
# Anbernic handheld port (suffix names the CFW it targets, so a
# future RG35XX/other-CFW pack can ship alongside it).
@@ -395,14 +393,11 @@ jobs:
"dist/release/gen1recomp-${v}-linux.zip"
"dist/release/gen1recomp-${v}-android.apk"
"dist/release/gen1recomp-${v}-ios.ipa"
"dist/release/gen1recomp-${v}-switch.nro"
"dist/release/gen1recomp-${v}-switch.zip"
"dist/release/gen1recomp-${v}-rg34xxsp-stockos64-mod.zip"
"dist/release/gen1recomp-${v}.love"
"dist/release/sha256sums.txt"
)
if [ -f "dist/release/gen1recomp-${v}-switch.nro.sha256" ]; then
release_files+=("dist/release/gen1recomp-${v}-switch.nro.sha256")
fi
gh release create "$tag" \
--target "$GITHUB_SHA" \
+3 -3
View File
@@ -231,15 +231,15 @@ Install steps, controls, and troubleshooting live in
## Nintendo Switch
Releases ship a fused `gen1recomp-*-switch.nro` (issue
Releases ship an SD-ready `gen1recomp-*-switch.zip` (issue
[#531](https://github.com/bryanthaboi/gen1recomp/issues/531)). Runtime target
is pinned [love-nx](https://github.com/retronx-team/love-nx) `11.5-nx1`.
Requires a console that can run Switch homebrew. Hardware evidence: **OLED**
(author) and **V1 / Erista** boot (community).
- Players: [docs/switch-install.md](docs/switch-install.md) — download the
NRO, copy to the SD, title-override launch, import your own legal ROM,
Joy-Con controls and shortcuts.
zip, extract at the microSD root (install or update), title-override
launch, import your own legal ROM, Joy-Con controls and shortcuts.
- Builders: [docs/switch-build.md](docs/switch-build.md) — `--fetch` /
`--loose` / `--fused`, toolchain, Docker fallback, and **CI vs release**
(path-gated ubuntu selftest, canonical fused PR artifact, release hard-fail).
+11 -8
View File
@@ -1,14 +1,14 @@
# Build the Nintendo Switch NRO — contributor guide
Want to play a release build instead? Download the fused NRO and copy it to
your console — see [switch-install.md](switch-install.md).
Want to play a release build instead? Download the SD-ready zip and extract it
at your microSD root — see [switch-install.md](switch-install.md).
This guide is for contributors who build Gen1Recomp for Switch from source.
Hardware evidence, MTP operator loops, and deeper notes live in
[switch-development.md](switch-development.md).
> Releases ship a fused `gen1recomp-*-switch.nro` (issue
> [#531](https://github.com/bryanthaboi/gen1recomp/issues/531)). Hardware
> Releases ship `gen1recomp-*-switch.zip` (SD tree under `switch/gen1recomp/`;
> issue [#531](https://github.com/bryanthaboi/gen1recomp/issues/531)). Hardware
> evidence: **OLED** (author) and **V1 boot** (community). See
> [switch-development.md](switch-development.md) for known limitations.
@@ -64,7 +64,7 @@ builds can fall back to the pinned image when native tools are missing.
| ---- | ------------ |
| `--fetch` | Downloads pinned **love.nro** + **love.elf** into `.bazinga/love-nx/11.5-nx1/` and verifies SHA-256 against `scripts/switch/love-nx-11.5-nx1.sha256`. |
| `--loose` | Packs `game.love`, copies pinned `love.nro` → `dist/switch/loose/` as `gen1recomp.nro` + `game.love` side by side. Needs the pin. |
| `--fused` | Builds a single `dist/switch/gen1recomp-<ver>-switch.nro` (game in romfs) via `nacptool` + `elf2nro`. Needs the pin + toolchain (native or Docker). |
| `--fused` | Builds `dist/switch/gen1recomp-<ver>-switch.nro` (game in romfs) via `nacptool` + `elf2nro`, then packs `dist/switch/gen1recomp-<ver>-switch.zip` (SD-ready tree). Needs the pin + toolchain (native or Docker). GitHub Releases publish the **zip only**. |
Rules:
@@ -109,12 +109,13 @@ scripts/build_switch.sh --fetch
# Loose pair for iteration (fetch + assemble)
scripts/build_switch.sh --fetch --loose
# Single fused NRO for a release-like artifact
# Single fused NRO + SD-ready zip for a release-like artifact
scripts/build_switch.sh --fetch --fused --version 0.2.0
```
Outputs land under `dist/switch/` (and `dist/switch/loose/` for loose mode).
The fused path also writes `gen1recomp-<ver>-switch.nro.sha256`.
The fused path also writes `gen1recomp-<ver>-switch.nro.sha256` and
`gen1recomp-<ver>-switch.zip` (+ `.sha256` sidecar for the zip).
Offline packaging smoke (no network, no nacptool required):
@@ -164,7 +165,9 @@ other platforms — this is a **hard gate** (no `continue-on-error`):
scripts/build_switch.sh --fetch --fused --version "<release version>"
```
A Switch packaging failure fails the entire release job.
A Switch packaging failure fails the entire release job. The release asset is
`gen1recomp-<ver>-switch.zip` (SD-ready); the versioned `.nro` stays under
`dist/switch/` for the packer and for PR CI artifacts.
### Runner provisioning
+6 -5
View File
@@ -1,8 +1,9 @@
# Nintendo Switch development (love-nx)
> Fused NRO support for issue [#531](https://github.com/bryanthaboi/gen1recomp/issues/531).
> Releases ship `gen1recomp-*-switch.nro`. Console copy is manual; title override
> required. See [Known limitations](#known-limitations-read-before-reviewing).
> Releases ship `gen1recomp-*-switch.zip` (SD-ready tree). Console copy is
> extract/merge at microSD root; title override required. See
> [Known limitations](#known-limitations-read-before-reviewing).
**Canonical install / build / transfer docs** (start here unless you need hardware depth):
@@ -27,9 +28,9 @@ the transfer runbook).
| ---- | ----- |
| Feature | **Available** — playable fused NRO path (issue #531) |
| Runtime | Pinned love-nx **`11.5-nx1`** |
| Product artifact | Single fused `gen1recomp.nro` (game in romfs); loose `nro`+`game.love` for iteration |
| Product artifact | Releases: SD-ready `gen1recomp-*-switch.zip`; local/PR: fused `.nro`; loose `nro`+`game.love` for iteration |
| Hardware | **OLED** validated (author, title override); **V1 / Erista** boot confirmed (community). Lite, docked soak, and Pro Controller matrices welcome |
| Deploy / install | Releases publish fused NRO; **console copy is manual** (MTP / SD / FTP — [switch-transfer.md](switch-transfer.md)); no `nxlink` path yet |
| Deploy / install | Releases publish SD-ready zip; **extract/merge at microSD root** (MTP / SD / FTP — [switch-transfer.md](switch-transfer.md)); no `nxlink` path yet |
| Contributor transfer | Documented for **macOS, Linux, and Windows**; OpenMTP on Mac is one example, not the only contract |
| Network features on NX | Self-update / remote mod download **disabled** (`networkValidated == false`) |
| Community help | Welcome — especially HOS / love-nx packaging and broader hardware coverage |
@@ -165,7 +166,7 @@ Detail for **macOS / Linux / Windows** and **MTP / SD / FTP** lives in
| Layer | Intent |
| ----- | ------ |
| **Runtime / players** | Put the NRO under `sdmc:/switch/gen1recomp/` and land ROMs/mods under the save-dir inboxes. The game does not hard-depend on OpenMTP or macOS. |
| **Runtime / players** | Extract the release zip at microSD root (`switch/gen1recomp/`) and land ROMs/mods under the save-dir inboxes. The game does not hard-depend on OpenMTP or macOS. |
| **Contributor loop** | Manual copy via MTP (DBI responder), direct SD (Hekate UMS / reader), or FTP. Fully manual — no CI deploy, no `nxlink` yet. |
The Mac + OpenMTP steps that remain below are the **OLED evidence reproduction** path; prefer the transfer runbook for day-to-day contrib on other hosts.
+1 -1
View File
@@ -127,7 +127,7 @@ Operator smoke for the switch-build-pipeline packaging CLI (closes matrix-deferr
| love-nx | `11.5-nx1` (manifest checksums match) |
| Artifact | `dist/switch/gen1recomp-0.0.0-test-switch.nro` |
| NRO SHA-256 | `210efb884a8d27443dc1c64ed8f071b0f862d8d0c9b140ad8185093c4e4027db` |
| Install doc | `docs/switch-install.md` — copy NRO under `sdmc:/switch/gen1recomp/` |
| Install doc | `docs/switch-install.md` at the time of this row: copy NRO under `sdmc:/switch/gen1recomp/` (releases now ship an SD-ready zip; same folder) |
| Console | Switch OLED |
| Operator | Andrew |
| Date | 2026-08-01 |
+26 -20
View File
@@ -1,8 +1,9 @@
# Install Gen1Recomp on Nintendo Switch
Every GitHub Release that includes Switch support ships a fused homebrew
binary: `gen1recomp-*-switch.nro`. Copy it to your microSD, launch with
**title override**, then import your own legal `.gb` ROM.
Every GitHub Release that includes Switch support ships an SD-ready zip:
`gen1recomp-*-switch.zip`. Extract it at the root of your microSD (install
**or** update — same steps), launch with **title override**, then import your
own legal `.gb` ROM.
> You need a console that can run Switch homebrew (custom firmware / hbmenu).
> This project does not help you set that up. Tracks issue
@@ -16,30 +17,36 @@ Prefer building from source? See [switch-build.md](switch-build.md).
Port by [andrewqsantos](https://github.com/andrewqsantos). Community testing
help from [booshankles](https://github.com/booshankles).
## 1. Download the NRO
## 1. Download the zip
1. Open
[Releases](https://github.com/bryanthaboi/gen1recomp/releases).
2. Download `gen1recomp-*-switch.nro` for the version you want.
(Optional: the matching `*.nro.sha256` sidecar if you want to verify the
download.)
2. Download `gen1recomp-*-switch.zip` for the version you want.
(Optional: verify against `sha256sums.txt` in the same release.)
## 2. Copy it to the microSD
## 2. Extract onto the microSD
Put the file here on the SD card:
Extract the zip at the **root** of the microSD so you get:
```text
sdmc:/switch/gen1recomp/gen1recomp.nro
sdmc:/switch/gen1recomp/pokemon-love2d/imports/
sdmc:/switch/gen1recomp/pokemon-love2d/imports/mods/
sdmc:/switch/gen1recomp/pokemon-love2d/imports/saves/...
```
(or keep the versioned name under `sdmc:/switch/gen1recomp/` — hbmenu will
list it either way).
Any method that lands the file on the SD is fine: **MTP** (DBI → Run MTP
Merge folders if your OS asks. Any method works: **MTP** (DBI → Run MTP
responder + a client), **direct SD** (Hekate UMS or a card reader), or **FTP**.
Exit MTP / unmount / stop FTP cleanly before launching. Step-by-step for
macOS, Linux, and Windows: [switch-transfer.md](switch-transfer.md).
### Updating
Use the **same** extract/merge. It replaces `gen1recomp.nro` (and the small
help `README.txt` / `INSTALL.txt` files). Saves, imported ROMs, mods, and
options live under `pokemon-love2d/`**do not delete that folder** when
updating, or you will lose progress.
## 3. Launch with title override
**Applet Mode is not supported** for this game (not enough memory).
@@ -55,15 +62,12 @@ Do **not** launch from the Album applet path for normal play.
This project ships **no** game data. On first launch:
1. Put your own legally obtained Pokémon Red or Blue `.gb` into the ROM
inbox under the games save directory (`imports/` the launcher shows
the live path).
1. Put your own legally obtained Pokémon Red or Blue `.gb` into
`switch/gen1recomp/pokemon-love2d/imports/` (the launcher also shows
the live save-dir path).
2. Use **Scan again** on the Red/Blue tab if you add the
file after the first open.
Saves live in the LÖVE save directory and **persist across NRO updates**
you can replace only the `.nro` and keep your progress.
## 5. Import / Export a raw `.sav`
Continue a cart or PC battery save (or pull a slot off-console) via MTP /
@@ -75,6 +79,8 @@ SD / FTP — same transfer methods as ROMs. Paths are **per game**:
| Blue | `imports/saves/blue/` | `exports/blue/` |
| Yellow | `imports/saves/yellow/` | `exports/yellow/` |
(Under the save dir `pokemon-love2d/` — the zip already creates these folders.)
1. Copy a Gen1 `.sav` (32 KB) into that games inbox under the save dir
([switch-transfer.md](switch-transfer.md)).
2. With the games ROM already imported, open **that games tab**
@@ -164,7 +170,7 @@ and
## Prefer building it yourself?
Building the fused (or loose) NRO from source is covered in
Building the fused NRO (and SD-ready zip) from source is covered in
[switch-build.md](switch-build.md). Copying artifacts and inbox files
(MTP / SD / FTP on macOS, Linux, Windows): [switch-transfer.md](switch-transfer.md).
Status, limitations, and how we tested: [switch-development.md](switch-development.md).
+8 -5
View File
@@ -19,7 +19,7 @@ Player install (what to download, title override) stays in
| What | Where on the console |
| ---- | -------------------- |
| Fused release NRO | `sdmc:/switch/gen1recomp/gen1recomp.nro` (or versioned name under that folder) |
| SD-ready release zip | Extract at microSD **root** `sdmc:/switch/gen1recomp/gen1recomp.nro` plus `pokemon-love2d/` inbox folders. Install and update use the same merge; do **not** delete `pokemon-love2d/` |
| Loose iteration pair | `sdmc:/switch/gen1recomp/gen1recomp.nro` **and** `game.love` beside it |
| ROM inbox | LÖVE save dir → `imports/` (launcher shows the live `getSaveDirectory()` path; under MTP often `1: SD Card/<save identity>/imports/`) |
| Mod zip inbox | Same save dir → `imports/mods/` then MODS → **Scan again** |
@@ -28,7 +28,8 @@ Player install (what to download, title override) stays in
| Opt-in diagnostics | Empty `switch-debug.txt` in the save dir → `switch.log` |
| Lua error log | `lua-error.log` in the save dir |
Saves persist across **NRO-only** replacements. Never commit ROM dumps, `.sav`
Saves persist across zip re-extract / NRO replacements as long as
`pokemon-love2d/` is left in place. Never commit ROM dumps, `.sav`
files, or third-party mod zips to git.
---
@@ -51,7 +52,8 @@ hardware evidence — **one contributor example**, not a Mac-only product rule.
1. Quit other MTP clients.
2. Open OpenMTP → select the DBI device → **`1: SD Card`**.
3. Create `switch/gen1recomp/` if needed; copy NRO (and `game.love` for loose).
3. Create `switch/gen1recomp/` if needed; extract the release zip at SD root
(or copy NRO / `game.love` for loose).
4. For ROMs/mods/saves, open the save-dir `imports/`, `imports/mods/`,
`imports/saves/<red|blue|yellow>/`, or `exports/<red|blue|yellow>/` path the
launcher prints.
@@ -68,7 +70,8 @@ fails to open.
desktops, or your distros KDE MTP stack).
2. With DBI MTP active, open **Files** / **Dolphin** / **Thunar** and select
the Switch / DBI device → **`1: SD Card`**.
3. Copy into `switch/gen1recomp/` and the save-dir inboxes as above.
3. Extract the release zip at SD root (merge), or copy into `switch/gen1recomp/`
and the save-dir inboxes as above.
4. Use **only one** MTP accessor at a time. If `mtp-tools` / `mtpfs` reports
“device is busy”, close the file managers MTP mount (or the CLI mount)
and retry with a single client.
@@ -81,7 +84,7 @@ card reader) or **FTP** instead — same destinations in the table above.
1. With DBI MTP active, open **This PC** / **File Explorer** and look under
**Portable Devices** for the Switch / DBI MTP volume → **`1: SD Card`**.
2. Copy files into `switch\gen1recomp\` and the save-dir inboxes.
2. Copy / extract into `switch\gen1recomp\` and the save-dir inboxes.
3. Optional: [OpenMTP](https://github.com/ganeshrvel/openmtp) on Windows if
Explorer is flaky.
4. If Windows does not show an MTP device: Device Manager → find DBI / Switch
+10 -5
View File
@@ -19,10 +19,13 @@
# (gen1recomp.nro + game.love side by side). Requires the pin
# (run --fetch first, or combine --fetch --loose).
#
# --fused Build a single gen1recomp-<ver>-switch.nro via nacptool+elf2nro.
# Uses native tools (PATH or $DEVKITPRO/tools/bin) first; else
# Docker from scripts/switch/dkp-docker.image (override
# GEN1_DKP_IMAGE). Requires the pin (run --fetch or combine).
# --fused Build a single gen1recomp-<ver>-switch.nro via nacptool+elf2nro,
# then pack dist/switch/gen1recomp-<ver>-switch.zip (SD-ready
# tree under switch/gen1recomp/). Uses native tools (PATH or
# $DEVKITPRO/tools/bin) first; else Docker from
# scripts/switch/dkp-docker.image (override GEN1_DKP_IMAGE).
# Requires the pin (run --fetch or combine). GitHub Releases
# publish the zip only; the versioned .nro stays local / PR CI.
#
# Combinable: --fetch alone, or --fetch with --loose / --fused.
# XOR: --loose and --fused cannot be used together.
@@ -46,7 +49,7 @@ say() { printf '\033[1;32m==>\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
usage() {
sed -n '2,32p' "$0" | sed 's/^# \{0,1\}//'
sed -n '2,34p' "$0" | sed 's/^# \{0,1\}//'
}
while [ $# -gt 0 ]; do
@@ -128,7 +131,9 @@ fi
if [ "$FUSED" -eq 1 ]; then
GAME_LOVE="$(pack_game_love)"
OUT_NRO="$DIST/gen1recomp-${VERSION}-switch.nro"
OUT_ZIP="$DIST/gen1recomp-${VERSION}-switch.zip"
"$ROOT/scripts/switch/build_fused.sh" "$GAME_LOVE" "$VERSION" "$OUT_NRO"
"$ROOT/scripts/switch/pack_sd_zip.sh" "$OUT_NRO" "$VERSION" "$OUT_ZIP"
cp "$WORK/build-info.json" "$DIST/gen1recomp-${VERSION}-build-info.json"
say "done. See $DIST/"
exit 0
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env bash
# Pack a Switch SD-ready zip: extract at microSD root (merge-safe update).
#
# Usage:
# scripts/switch/pack_sd_zip.sh NRO_PATH VERSION OUT_ZIP
#
# Layout inside the zip (SD root):
# switch/gen1recomp/gen1recomp.nro
# switch/gen1recomp/INSTALL.txt
# switch/gen1recomp/pokemon-love2d/imports/.../README.txt
# switch/gen1recomp/pokemon-love2d/exports/.../README.txt
#
# Does not ship ROMs, saves, or mods. Re-extracting merges over an existing
# install and only overwrites the NRO + these text placeholders — keep
# pokemon-love2d/ to preserve progress.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=common.sh
. "$SCRIPT_DIR/common.sh"
NRO_PATH="${1:-}"
VERSION="${2:-}"
OUT_ZIP="${3:-}"
[ -n "$NRO_PATH" ] && [ -n "$VERSION" ] && [ -n "$OUT_ZIP" ] \
|| fail "usage: scripts/switch/pack_sd_zip.sh NRO_PATH VERSION OUT_ZIP"
[ -f "$NRO_PATH" ] || fail "missing NRO at $NRO_PATH"
command -v zip >/dev/null 2>&1 || fail "need zip on PATH"
# Absolutize before any cd — relative OUT_ZIP would otherwise land inside the
# staging dir and vanish when the EXIT trap cleans up.
NRO_PATH="$(cd "$(dirname "$NRO_PATH")" && pwd)/$(basename "$NRO_PATH")"
OUT_DIR="$(dirname "$OUT_ZIP")"
mkdir -p "$OUT_DIR"
OUT_ZIP="$(cd "$OUT_DIR" && pwd)/$(basename "$OUT_ZIP")"
STAGE="$(mktemp -d "${TMPDIR:-/tmp}/gen1recomp-sd-zip.XXXXXX")"
cleanup() { rm -rf "$STAGE"; }
trap cleanup EXIT
APP_DIR="$STAGE/switch/gen1recomp"
SAVE_ROOT="$APP_DIR/pokemon-love2d"
mkdir -p "$APP_DIR"
cp "$NRO_PATH" "$APP_DIR/gen1recomp.nro"
cat > "$APP_DIR/INSTALL.txt" <<EOF
gen1recomp Switch (v${VERSION})
===============================
First install or update (same steps):
1. Extract this zip at the root of your microSD (merge folders if asked).
2. Do NOT delete switch/gen1recomp/pokemon-love2d/ — that folder holds
your saves, imported ROMs, mods, and options. Re-extracting only
replaces gen1recomp.nro and these help files.
3. Launch with title override (hold R on HOME, open any title → hbmenu).
4. Copy a legal Pokemon Red/Blue .gb into:
switch/gen1recomp/pokemon-love2d/imports/
then use Scan again in the launcher if needed.
Inboxes (drop files here via MTP / SD / FTP):
imports/ — ROM .gb / .gbc
imports/mods/ — community mod .zip
imports/saves/red|blue|yellow/ — raw .sav import
exports/red|blue|yellow/ — pull after Export save
Full guide: https://github.com/bryanthaboi/gen1recomp/blob/main/docs/switch-install.md
EOF
write_readme() {
local path="$1"
local body="$2"
mkdir -p "$(dirname "$path")"
printf '%s\n' "$body" > "$path"
}
write_readme "$SAVE_ROOT/imports/README.txt" \
"Put a legal Pokemon Red or Blue .gb / .gbc here, then Scan again in the launcher."
write_readme "$SAVE_ROOT/imports/mods/README.txt" \
"Put community mod .zip files here, then MODS → Scan again."
write_readme "$SAVE_ROOT/imports/saves/red/README.txt" \
"Put a Red .sav (32 KB) here, then Red tab → SAVE FILES → Import save."
write_readme "$SAVE_ROOT/imports/saves/blue/README.txt" \
"Put a Blue .sav (32 KB) here, then Blue tab → SAVE FILES → Import save."
write_readme "$SAVE_ROOT/imports/saves/yellow/README.txt" \
"Put a Yellow .sav (32 KB) here, then Yellow tab → SAVE FILES → Import save."
write_readme "$SAVE_ROOT/exports/red/README.txt" \
"After Export save (Red), copy the .sav out of this folder via MTP / SD / FTP."
write_readme "$SAVE_ROOT/exports/blue/README.txt" \
"After Export save (Blue), copy the .sav out of this folder via MTP / SD / FTP."
write_readme "$SAVE_ROOT/exports/yellow/README.txt" \
"After Export save (Yellow), copy the .sav out of this folder via MTP / SD / FTP."
rm -f "$OUT_ZIP"
(
cd "$STAGE"
zip -q -r "$OUT_ZIP" switch
)
[ -f "$OUT_ZIP" ] || fail "zip was not created at $OUT_ZIP"
[ -s "$OUT_ZIP" ] || fail "zip is empty: $OUT_ZIP"
LISTING="$(unzip -Z1 "$OUT_ZIP" 2>/dev/null || unzip -l "$OUT_ZIP")"
printf '%s\n' "$LISTING" | grep -q 'switch/gen1recomp/gen1recomp.nro' \
|| fail "zip missing switch/gen1recomp/gen1recomp.nro"
REQUIRED=(
"switch/gen1recomp/INSTALL.txt"
"switch/gen1recomp/pokemon-love2d/imports/README.txt"
"switch/gen1recomp/pokemon-love2d/imports/mods/README.txt"
"switch/gen1recomp/pokemon-love2d/imports/saves/red/README.txt"
"switch/gen1recomp/pokemon-love2d/imports/saves/blue/README.txt"
"switch/gen1recomp/pokemon-love2d/imports/saves/yellow/README.txt"
"switch/gen1recomp/pokemon-love2d/exports/red/README.txt"
"switch/gen1recomp/pokemon-love2d/exports/blue/README.txt"
"switch/gen1recomp/pokemon-love2d/exports/yellow/README.txt"
)
for rel in "${REQUIRED[@]}"; do
printf '%s\n' "$LISTING" | grep -Fq "$rel" || fail "zip missing $rel"
done
ZIP_SHA="$(sha256_file "$OUT_ZIP")"
printf '%s %s\n' "$ZIP_SHA" "$OUT_ZIP" > "${OUT_ZIP}.sha256"
say "SD-ready zip: $OUT_ZIP"
printf '%s %s\n' "$ZIP_SHA" "$OUT_ZIP"
+104 -2
View File
@@ -4,8 +4,8 @@
# Usage: scripts/switch/selftest_build_switch.sh
#
# Covers: sha256_file, --help glossary, XOR loose/fused, fail_need_fetch,
# verify_love_nx mismatch, fail_fused_toolchain message. Does not download
# love-nx or invoke nacptool/elf2nro/Docker.
# verify_love_nx mismatch, fail_fused_toolchain message, pack_sd_zip layout.
# Does not download love-nx or invoke nacptool/elf2nro/Docker.
set -euo pipefail
@@ -209,6 +209,108 @@ else
bad "fail_fused_toolchain missing OS hints:$OS_MISSING"
fi
# ---------------------------------------------------------------------------
# 6. pack_sd_zip.sh builds SD-ready tree (offline; fake NRO)
# ---------------------------------------------------------------------------
FAKE_NRO="$STAGING/fake.nro"
printf 'fake-nro-bytes\n' > "$FAKE_NRO"
FAKE_ZIP="$STAGING/gen1recomp-0.0.0-test-switch.zip"
PACK_RC=0
"$ROOT/scripts/switch/pack_sd_zip.sh" "$FAKE_NRO" "0.0.0-test" "$FAKE_ZIP" \
>"$STAGING/pack.out" 2>"$STAGING/pack.err" || PACK_RC=$?
if [ "$PACK_RC" -eq 0 ] && [ -f "$FAKE_ZIP" ] && [ -s "$FAKE_ZIP" ]; then
ok "pack_sd_zip.sh writes a non-empty zip"
else
bad "pack_sd_zip.sh failed (rc=$PACK_RC err=$(cat "$STAGING/pack.err"))"
fi
ZIP_LIST="$(unzip -Z1 "$FAKE_ZIP" 2>/dev/null || unzip -l "$FAKE_ZIP")"
PACK_MISSING=""
for rel in \
"switch/gen1recomp/gen1recomp.nro" \
"switch/gen1recomp/INSTALL.txt" \
"switch/gen1recomp/pokemon-love2d/imports/README.txt" \
"switch/gen1recomp/pokemon-love2d/imports/mods/README.txt" \
"switch/gen1recomp/pokemon-love2d/imports/saves/red/README.txt" \
"switch/gen1recomp/pokemon-love2d/imports/saves/blue/README.txt" \
"switch/gen1recomp/pokemon-love2d/imports/saves/yellow/README.txt" \
"switch/gen1recomp/pokemon-love2d/exports/red/README.txt" \
"switch/gen1recomp/pokemon-love2d/exports/blue/README.txt" \
"switch/gen1recomp/pokemon-love2d/exports/yellow/README.txt"
do
printf '%s\n' "$ZIP_LIST" | grep -Fq "$rel" || PACK_MISSING="${PACK_MISSING} ${rel}"
done
if [ -z "$PACK_MISSING" ]; then
ok "pack_sd_zip.sh zip contains SD tree + inbox READMEs"
else
bad "pack_sd_zip.sh zip missing:$PACK_MISSING"
fi
EXTRACT_DIR="$STAGING/extract-v1"
rm -rf "$EXTRACT_DIR"
mkdir -p "$EXTRACT_DIR"
unzip -q "$FAKE_ZIP" -d "$EXTRACT_DIR"
if cmp -s "$FAKE_NRO" "$EXTRACT_DIR/switch/gen1recomp/gen1recomp.nro"; then
ok "pack_sd_zip.sh NRO bytes match source"
else
bad "pack_sd_zip.sh NRO inside zip differs from source"
fi
if [ -f "${FAKE_ZIP}.sha256" ]; then
ok "pack_sd_zip.sh writes .sha256 sidecar"
else
bad "pack_sd_zip.sh should write ${FAKE_ZIP}.sha256"
fi
MISSING_NRO_RC=0
"$ROOT/scripts/switch/pack_sd_zip.sh" "$STAGING/does-not-exist.nro" "0.0.0" \
"$STAGING/should-fail.zip" >/dev/null 2>&1 || MISSING_NRO_RC=$?
if [ "$MISSING_NRO_RC" -ne 0 ]; then
ok "pack_sd_zip.sh fails when NRO is missing"
else
bad "pack_sd_zip.sh should fail on missing NRO"
fi
# Relative OUT_ZIP must survive (absolutized before cd into staging)
REL_DIR="$STAGING/rel-out"
mkdir -p "$REL_DIR"
REL_RC=0
(
cd "$REL_DIR"
"$ROOT/scripts/switch/pack_sd_zip.sh" "$FAKE_NRO" "0.0.1" "relative.zip" \
>"$STAGING/rel.out" 2>"$STAGING/rel.err"
) || REL_RC=$?
if [ "$REL_RC" -eq 0 ] && [ -f "$REL_DIR/relative.zip" ] && [ -s "$REL_DIR/relative.zip" ]; then
ok "pack_sd_zip.sh accepts relative OUT_ZIP"
else
bad "pack_sd_zip.sh relative OUT_ZIP failed (rc=$REL_RC err=$(cat "$STAGING/rel.err"))"
fi
# Merge-safe update: second extract replaces NRO, keeps user data
printf 'KEEP-SAVE' > "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/slot.sav"
printf 'KEEP-ROM' > "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/imports/red.gb"
printf 'KEEP-MOD' > "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/imports/mods/mod.zip"
printf 'KEEP-OPTS' > "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/options.lua"
FAKE_NRO2="$STAGING/fake-v2.nro"
printf 'fake-nro-bytes-v2\n' > "$FAKE_NRO2"
FAKE_ZIP2="$STAGING/gen1recomp-0.0.1-test-switch.zip"
"$ROOT/scripts/switch/pack_sd_zip.sh" "$FAKE_NRO2" "0.0.1-test" "$FAKE_ZIP2" \
>"$STAGING/pack2.out" 2>"$STAGING/pack2.err"
unzip -qo "$FAKE_ZIP2" -d "$EXTRACT_DIR"
MERGE_OK=1
cmp -s "$FAKE_NRO2" "$EXTRACT_DIR/switch/gen1recomp/gen1recomp.nro" || MERGE_OK=0
[ "$(cat "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/slot.sav")" = "KEEP-SAVE" ] || MERGE_OK=0
[ "$(cat "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/imports/red.gb")" = "KEEP-ROM" ] || MERGE_OK=0
[ "$(cat "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/imports/mods/mod.zip")" = "KEEP-MOD" ] || MERGE_OK=0
[ "$(cat "$EXTRACT_DIR/switch/gen1recomp/pokemon-love2d/options.lua")" = "KEEP-OPTS" ] || MERGE_OK=0
if [ "$MERGE_OK" -eq 1 ]; then
ok "pack_sd_zip.sh merge update preserves user data"
else
bad "pack_sd_zip.sh merge update lost user data or failed to replace NRO"
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
+24
View File
@@ -172,4 +172,28 @@ do
mustContain(block, "Hard-fail", "release Build Switch comment")
end
-- Release publishes SD-ready zip only (no bare .nro / .nro.sha256 assets)
do
local start = release:find("- name: Stage release assets", 1, true)
check(start ~= nil, "release Stage release assets present")
local rest = release:sub(start)
local nextStep = rest:find("\n - name:", 2)
local block = nextStep and rest:sub(1, nextStep - 1) or rest
mustContain(block, "gen1recomp-${v}-switch.zip", "release Stage Switch zip")
mustContain(block, "pack_sd_zip.sh", "release Stage cites pack_sd_zip")
mustNotContain(block, "gen1recomp-${v}-switch.nro", "release Stage no bare NRO")
mustNotContain(block, "switch.nro.sha256", "release Stage no NRO sha256 sidecar")
end
do
local start = release:find("- name: Publish GitHub Release", 1, true)
check(start ~= nil, "release Publish GitHub Release present")
local rest = release:sub(start)
local nextStep = rest:find("\n - name:", 2)
local block = nextStep and rest:sub(1, nextStep - 1) or rest
mustContain(block, "gen1recomp-${v}-switch.zip", "release Publish Switch zip")
mustNotContain(block, "gen1recomp-${v}-switch.nro", "release Publish no bare NRO")
mustNotContain(block, "switch.nro.sha256", "release Publish no NRO sha256 sidecar")
end
T.finish("switch_ci_workflows_test")