Tighten Switch docs prose and drop filler patterns.

This commit is contained in:
Andrew Quenehen
2026-08-06 08:43:32 -03:00
parent ecfca19f11
commit c7e72149f1
13 changed files with 102 additions and 104 deletions
+15 -19
View File
@@ -1,7 +1,7 @@
# Build the Nintendo Switch NRO — contributor guide
# Build Gen1Recomp for Nintendo Switch
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).
at your microSD root. See [switch-install.md](switch-install.md).
This guide is for contributors who build Gen1Recomp for Switch from source.
@@ -14,7 +14,7 @@ This guide is for contributors who build Gen1Recomp for Switch from source.
## Prerequisites by OS
All packaging entrypoints are **bash**. On Windows, use Git Bash, MSYS2, or
WSL not cmd.exe or PowerShell (AD-008).
WSL, not cmd.exe or PowerShell (AD-008).
### macOS / Linux
@@ -25,7 +25,7 @@ WSL — not cmd.exe or PowerShell (AD-008).
sudo dkp-pacman -S switch-dev
```
3. OTA launcher toolchain **native or Docker** (either is fine):
3. OTA launcher toolchain, **native or Docker** (either is fine):
```sh
bash scripts/switch/install_devkitpro_deps.sh # native
@@ -51,7 +51,7 @@ scripts/switch/build_ota_launcher.sh # host-test first; NRO needs DEVKITPRO/Do
`--fused` always builds the fused game, native OTA launcher, and dual-NRO SD
zip. The same `*-switch.zip` is the OTA download asset. **DEVKITPRO is
required.** OTA launcher: native packages **or** Docker — both are supported.
required.** OTA launcher: native packages **or** Docker. Both are supported.
Release-like build from repo root:
@@ -70,14 +70,14 @@ See `native/switch-ota-launcher/README.md` and
- **WSL** (Ubuntu/etc.) with the Linux pacman flow above, or
- **Git Bash** for `--fetch` / `--loose`; for `--fused` prefer MSYS2 or
WSL if Docker bind-mounts from Git Bash paths misbehave.
2. Install `switch-dev` (or rely on Docker fallback see below).
2. Install `switch-dev` (or rely on Docker fallback; see below).
3. Do **not** expect `scripts/build_switch.sh` to run under cmd/PowerShell.
### What you must install yourself
| You install | Script does **not** install |
| ----------- | --------------------------- |
| bash, git, zip tooling the repo already expects | |
| bash, git, zip tooling the repo already expects | (none) |
| `dkp-pacman` + `switch-dev` + OTA packages **or** Docker | `dkp-pacman -S …` |
| A legal `.gb` ROM (to play) | Any ROM or game data |
@@ -96,7 +96,7 @@ See `native/switch-ota-launcher/README.md` and
Rules:
- `--fetch` alone is fine; combine as `--fetch --loose` or `--fetch --fused`.
- `--loose` and `--fused` are **XOR** — pick one packaging path per run.
- `--loose` and `--fused` are **XOR**. Pick one packaging path per run.
- `--version X.Y.Z` sets the NACP / filename version (defaults to short git SHA).
### What `--fetch` downloads
@@ -172,7 +172,7 @@ the NX runtime modules `src/core/NxAssetOverlay.lua`, `src/core/Platform.lua`,
`tests/engine/switch_diagnostics_test.lua`, `tests/engine/platform_nx_*`,
or the Switch-related workflow YAML), CI runs:
1. **Offline selftest** on `ubuntu-latest` (forks **and** the canonical repo):
1. **Offline selftest** on `ubuntu-latest` (forks **and** the main repo):
`scripts/switch/selftest_build_switch.sh`,
`scripts/switch/verify_payload.sh --self-test`,
`luajit tests/switch_ci_workflows_test.lua`,
@@ -180,14 +180,10 @@ or the Switch-related workflow YAML), CI runs:
headlessly (`luajit tests/engine/assets_version_fallback_test.lua`,
`luajit tests/engine/nx_generated_guard_test.lua`,
`luajit tests/engine/nx_yellow_boot_test.lua`).
2. **Fused NRO build** only on the **canonical** repository
2. **Fused NRO build** only on the **main** repository
(`bryanthaboi/gen1recomp`), on the self-hosted Mac runner
(`scripts/build_switch.sh --fetch --fused`), and only when the workflow
head is that repo (same-repo push/PR). **Fork repository** CI never runs
fused. **Fork → canonical PRs** also skip Switch fused (offline selftest
still runs) so untrusted head code is not executed on the self-hosted Mac;
iOS device build eligibility is unchanged. Fused also waits for a successful
offline selftest before starting on the Mac runner.
head is that repo (same-repo push/PR). Fork CI never runs fused. Fork PRs into the main repo also skip Switch fused (offline selftest still runs) so untrusted head code is not executed on the self-hosted Mac; iOS device build eligibility is unchanged. Fused also waits for a successful offline selftest before starting on the Mac runner.
3. On successful PR fused builds, a follow-up workflow posts a PR comment
linking the Actions artifact named `gen1recomp-switch-nro`
(comment tag `switch-build-result`; see
@@ -198,7 +194,7 @@ Unrelated PRs do not burn the self-hosted Mac on Switch packaging.
### Release hard-fail (`.github/workflows/release.yml`)
GitHub Releases always build Switch on the same self-hosted Mac runner as the
other platforms — this is a **hard gate** (no `continue-on-error`):
other platforms. This is a **hard gate** (no `continue-on-error`):
```sh
scripts/build_switch.sh --fetch --fused --version "<release version>"
@@ -221,7 +217,7 @@ sudo dkp-pacman -S switch-dev
export DEVKITPRO=/opt/devkitpro
export PATH="$DEVKITPRO/tools/bin:$PATH"
# OTA launcher pick one:
# OTA launcher: pick one
bash scripts/switch/install_devkitpro_deps.sh # native
# or ensure Docker is installed (same pin as fused builds)
```
@@ -240,8 +236,8 @@ These scripts and this guide do **not**:
- Push files to the console (no automated MTP / FTP / SD scripting)
- Bundle or download any Pokémon ROM
- Install `dkp-pacman` / `switch-dev` for you
- Provide `nxlink` / netloader deploy (deferred see [switch-transfer.md](switch-transfer.md))
- Validate **Applet Mode** — use title override (hold **R**) for full memory
- Provide `nxlink` / netloader deploy (deferred; see [switch-transfer.md](switch-transfer.md))
- Validate **Applet Mode**. Use title override (hold **R**) for full memory
Player install steps: [switch-install.md](switch-install.md).
Manual transfer (MTP / SD / FTP, macOS / Linux / Windows):
+39 -38
View File
@@ -2,7 +2,7 @@
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
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).
@@ -34,7 +34,8 @@ sdmc:/switch/gen1recomp/pokemon-love2d/imports/saves/...
```
Older single-NRO zips only had `gen1recomp.nro` (the fused game). Current
releases use the dual-NRO layout above — open `gen1recomp` in hbmenu (the launcher).
releases use the dual-NRO layout above. Open `gen1recomp` in hbmenu (the
launcher).
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**.
@@ -45,25 +46,25 @@ macOS, Linux, and Windows: [switch-transfer.md](switch-transfer.md).
#### Native OTA launcher (in-console)
On Switch, over-the-air updates are handled by the **native OTA launcher**
(DEVKITPRO / libnx + curl), **not** by the LÖVE self-updater
(`src/update/Check.lua`). The hbmenu entry is the launcher NRO
(`gen1recomp.nro`). If a newer release exists it may download the same
`gen1recomp-*-switch.zip` used for install, verify SHA-256 from
`sha256sums.txt`, replace **both** `gen1recomp-game.nro` and
`gen1recomp.nro` (so the NACP version matches the release for hbmenu /
Sphaira), then hand off via `envSetNextLoad`. When you are already up to
date (or offline), the launcher stays quiet and opens the game with no
UI. When an update is available it shows a short screen in the same visual
language as the in-game launcher (black field, RGB rail, logo, A/B buttons).
Saves under `pokemon-love2d/` are never touched. Protocol
contract: `src/update/SwitchOta.lua`.
Switch OTA runs in a separate **native launcher NRO** (libnx + curl), not the
LÖVE self-updater (`src/update/Check.lua`). hbmenu opens `gen1recomp.nro`.
When a newer release exists, the launcher downloads the same install zip
(`gen1recomp-*-switch.zip`), checks SHA-256 against `sha256sums.txt`, replaces
both `gen1recomp-game.nro` and `gen1recomp.nro` (keeps NACP version in sync
for hbmenu and Sphaira), then loads the game with `envSetNextLoad`.
If you are up to date or offline, it skips straight to the game with no
prompt. If an update is available, you get a short prompt styled like the
in-game launcher: black background, RGB rail, logo, A/B buttons. Saves under
`pokemon-love2d/` are not touched. See `src/update/SwitchOta.lua` for the
wire format.
The LÖVE self-updater stays **disabled** on NX (`networkValidated == false`).
**Sphaira forwarder (HOME shortcut):** Sphaira copies name/version/icon into
the installed forwarder at creation time. After an OTA (or zip) update, the
`.nro` on the microSD already has the new version but the HOME shortcut
`.nro` on the microSD already has the new version, but the HOME shortcut
keeps the old badge until you **reinstall the forwarder once** in Sphaira
(Install Forwarder again on `gen1recomp.nro`). Browsing the NRO in Sphaira /
hbmenu always shows the live file version.
@@ -72,7 +73,7 @@ hbmenu always shows the live file version.
Use the **same** extract/merge of `gen1recomp-*-switch.zip`. It replaces the
NROs (and the small help `README.txt` / `INSTALL.txt` files). Saves,
imported ROMs, mods, and options live under `pokemon-love2d/` **do not
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
@@ -80,7 +81,7 @@ delete that folder** when updating, or you will lose progress.
**Applet Mode is not supported** for this game (not enough memory).
1. On the Switch HOME menu, highlight any installed title.
2. Hold **R** and launch that title — this opens hbmenu with full memory
2. Hold **R** and launch that title. This opens hbmenu with full memory
(title override).
3. From hbmenu, open `gen1recomp`.
@@ -94,14 +95,14 @@ This project ships **no** game data. On first launch:
(`.gbc`) dump into `switch/gen1recomp/pokemon-love2d/imports/` (the
launcher also shows the live save-dir path). All three can sit in the
same folder.
2. Use **Scan again** on that games tab (Red / Blue / Yellow). Rescan
matches by ROM SHA-1 for the open tab only — a Red dump never imports
2. Use **Scan again** on that game's tab (Red / Blue / Yellow). Rescan
matches by ROM SHA-1 for the open tab only. A Red dump never imports
from the Yellow tab (and vice versa).
## 5. Import / Export a raw `.sav`
Continue a cart or PC battery save (or pull a slot off-console) via MTP /
SD / FTP same transfer methods as ROMs. Paths are **per game**:
SD / FTP, same transfer methods as ROMs. Paths are **per game**:
| Game | Import inbox | Export folder |
| ---- | ------------ | ------------- |
@@ -109,19 +110,19 @@ 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.)
(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
1. Copy a Gen1 `.sav` (32 KB) into that game's inbox under the save dir
([switch-transfer.md](switch-transfer.md)).
2. With the games ROM already imported, open **that games tab**
2. With the game's ROM already imported, open **that game's tab**
**SAVE FILES****Import save**. Only that folder is scanned.
3. A successful import retires the file to `*.sav.imported` and records its
content hash so pressing **Import save** again does not clone slots.
Failed imports leave the original `.sav` in place.
4. To pull a slot off the console, use **Export save**, then copy the file
from that games **`exports/<game>/`** folder via MTP / SD / FTP.
from that game's **`exports/<game>/`** folder via MTP / SD / FTP.
Do not put `.sav` files into git. Prefer clean copies — some MTP clients
Do not put `.sav` files into git. Prefer clean copies. Some MTP clients
create `._*.sav` AppleDouble sidecars that are not real saves.
## Controls
@@ -158,12 +159,12 @@ create `._*.sav` AppleDouble sidecars that are not real saves.
Mods install from a zip inbox (same transfer methods as ROMs):
1. Copy a release `.zip` into the save-dir **`imports/mods/`** path the
launcher shows (MTP / SD / FTP [switch-transfer.md](switch-transfer.md)).
launcher shows (MTP / SD / FTP. See [switch-transfer.md](switch-transfer.md)).
2. In the launcher, open **MODS****Scan again** → enable the mod →
**Play**.
Remote **FIND MODS** / GitHub download stays **off** on Switch. Do not put
mod zips into git. Community mods ship their own OPTIONS / rebinds — this port
mod zips into git. Community mods ship their own OPTIONS / rebinds. This port
does not document third-party control tables.
### Joy-Con shortcuts (Select + face)
@@ -185,16 +186,16 @@ If the handheld stutters with extras on, try **OPTIONS → PERFORMANCE** →
## Limitations
- **Homebrew required** — custom firmware and hbmenu; this project does not help
set that up.
- **Title override required** — hold **R** when launching a title for full
memory. Applet Mode (Album) is not supported.
- **Manual file transfer** — ROMs, mods, and saves are copied via MTP, direct
SD, or FTP; there is no automated deploy.
- **No LÖVE self-updater** — in-console updates use the native OTA launcher
only. Remote **FIND MODS** / GitHub download stays off on Switch.
- **Hardware** — tested on Switch OLED; Switch V1 / Erista boot confirmed by the
community. Other models may work but are less tested.
- You need homebrew (custom firmware, hbmenu). This project does not set that
up.
- Launch with title override (hold **R** on a title). Applet Mode (Album) is
not supported. The game needs full memory.
- ROMs, mods, and saves are copied manually via MTP, direct SD, or FTP. There
is no automated deploy.
- Updates use the native OTA launcher only. The LÖVE self-updater and remote
**FIND MODS** stay off on Switch.
- Tested on Switch OLED. Switch V1 / Erista boot confirmed by the community.
Other models may work but are less tested.
## Prefer building it yourself?
+17 -17
View File
@@ -1,16 +1,16 @@
# Switch file transfer (MTP / SD / FTP)
Canonical ways to put Gen1Recomp artifacts and inbox files onto a Nintendo
Switch. **Any method is valid** if the bytes land in the destinations below.
Ways to put Gen1Recomp artifacts and inbox files onto a Nintendo Switch.
**Any method is valid** if the bytes land in the destinations below.
This is the home runbook for contributors on **macOS, Linux, and Windows**.
This is the runbook for contributors on **macOS, Linux, and Windows**.
Player install (what to download, title override) stays in
[switch-install.md](switch-install.md). Packaging stays in
[switch-build.md](switch-build.md).
> **Not supported yet:** `nxlink` / hbmenu netloader automation. Useful later
> for a fast contrib rebuild loop; deferred on purpose (AD-009). Do not treat
> netloader as the release or ROM/mod install path.
> for a fast contrib rebuild loop; deferred (AD-009). Do not treat netloader as
> the release or ROM/mod install path.
---
@@ -22,7 +22,7 @@ Player install (what to download, title override) stays in
| 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** |
| Save `.sav` inbox | Same save dir → `imports/saves/red\|blue\|yellow/` then that games SAVE FILES → **Import save** |
| Save `.sav` inbox | Same save dir → `imports/saves/red\|blue\|yellow/` then that game's SAVE FILES → **Import save** |
| Save exports | Same save dir → `exports/red\|blue\|yellow/` (pull after **Export save**; MTP / SD / FTP) |
| Opt-in diagnostics | Empty `switch-debug.txt` in the save dir → `switch.log` |
| Lua error log | `lua-error.log` in the save dir |
@@ -33,7 +33,7 @@ files, or third-party mod zips to git.
---
## Canonical methods
## Transfer methods
### 1. MTP (DBI responder + host client)
@@ -47,7 +47,7 @@ before launching.
#### macOS (example: OpenMTP)
[OpenMTP](https://github.com/ganeshrvel/openmtp) is a documented example for
macOS**one contributor workflow**, not a Mac-only product rule.
macOS, not a Mac-only requirement.
1. Quit other MTP clients.
2. Open OpenMTP → select the DBI device → **`1: SD Card`**.
@@ -59,25 +59,25 @@ macOS — **one contributor workflow**, not a Mac-only product rule.
5. Wait for the queue; refresh; exit MTP responder; title-override launch.
macOS clients often create AppleDouble sidecars (`._Something.zip`,
`._cart.gb`, `._foo.sav`). Those are not real archives or saves — the
`._cart.gb`, `._foo.sav`). Those are not real archives or saves. The
launcher skips hidden `.*` names. Delete `._*` junk if a zip/ROM/`.sav`
fails to open.
#### Linux
1. Install desktop MTP support if needed (e.g. `gvfs-mtp` on GNOME/GTK
desktops, or your distros KDE MTP stack).
desktops, or your distro's KDE MTP stack).
2. With DBI MTP active, open **Files** / **Dolphin** / **Thunar** and select
the Switch / DBI device → **`1: SD Card`**.
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)
"device is busy", close the file manager's MTP mount (or the CLI mount)
and retry with a single client.
5. Eject/unmount cleanly; exit MTP on the Switch; title-override launch.
If MTP is unavailable or flaky on Linux, use **direct SD** (Hekate UMS or a
card reader) or **FTP** instead — same destinations in the table above.
card reader) or **FTP** instead. Same destinations in the table above.
#### Windows
@@ -92,7 +92,7 @@ card reader) or **FTP** instead — same destinations in the table above.
5. Safely disconnect; exit MTP on the Switch; title-override launch.
If MTP is unavailable or flaky on Windows, use **direct SD** (Hekate UMS or a
card reader) or **FTP** instead — same destinations in the table above.
card reader) or **FTP** instead. Same destinations in the table above.
### 2. Direct SD (Hekate UMS or card reader)
@@ -108,9 +108,9 @@ Do not yank the card or unplug UMS mid-write.
### 3. FTP (any SD-exposing Switch FTP)
Any homebrew FTP server that can write the microSD is fine — for example
**DBIs own FTP**, **sys-ftpd-light**, or **Sphaira** (names are illustrations
only; pick what your CFW setup already uses).
Any homebrew FTP server that can write the microSD is fine. For example
**DBI's own FTP**, **sys-ftpd-light**, or **Sphaira**. Names are illustrations
only; pick what your CFW setup already uses.
1. Start the FTP server on the Switch; note IP/port/credentials from that app.
2. From the host, connect with any FTP client and upload to the same
@@ -153,7 +153,7 @@ Copy the file back from the SD and compare hashes. Round-trip must match.
| Symptom | What to try |
| ------- | ----------- |
| Device busy / no MTP volume | One client only; different cable/port; Windows MTP USB Device driver; alternate method (SD or FTP) |
| Zip/ROM/`.sav` could not be opened | Delete `._*` sidecars (including `._*.sav`); confirm real zip starts with `PK` |
| Zip/ROM/`.sav` "could not be opened" | Delete `._*` sidecars (including `._*.sav`); confirm real zip starts with `PK` |
| Half-copied NRO / crash on boot | Re-copy; verify SHA-256; exit transfer mode before launch |
| App opens in Applet Mode | Use title override (hold **R**), not Album |
+3 -3
View File
@@ -126,6 +126,6 @@ bundled game, in that case.
checkout is always "the game" itself; updating it means pulling the repo.
- **Nintendo Switch does not use this LÖVE self-updater.** On NX,
`Platform.networkValidated()` is `false`, so `Boot.run` / `Check` never
download `.love` payloads. In-console OTA is the **native OTA launcher**
(DEVKITPRO) documented in [switch-install.md](switch-install.md); protocol contract
`src/update/SwitchOta.lua`. Manual zip install remains the fallback.
download `.love` payloads. In-console OTA uses the **native OTA launcher**
(DEVKITPRO), documented in [switch-install.md](switch-install.md). Wire
format: `src/update/SwitchOta.lua`. Manual zip install remains the fallback.