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
+5 -4
View File
@@ -235,12 +235,13 @@ Releases ship an SD-ready `gen1recomp-*-switch.zip`. Runtime target is pinned
[love-nx](https://github.com/retronx-team/love-nx) `11.5-nx1`. Requires a [love-nx](https://github.com/retronx-team/love-nx) `11.5-nx1`. Requires a
console that can run Switch homebrew. console that can run Switch homebrew.
- Players: [docs/switch-install.md](docs/switch-install.md) — download the - Players: [docs/switch-install.md](docs/switch-install.md). Download the
zip, extract at the microSD root (install or update), title-override zip, extract at the microSD root (install or update), title-override
launch, import your own legal ROM, Joy-Con controls and shortcuts. launch, import your own legal ROM, Joy-Con controls and shortcuts.
- Builders: [docs/switch-build.md](docs/switch-build.md) `--fetch` / - Builders: [docs/switch-build.md](docs/switch-build.md). `--fetch` /
`--loose` / `--fused`, toolchain, Docker fallback, and **CI vs release** `--loose` / `--fused`, toolchain, Docker fallback, and CI vs release
(path-gated ubuntu selftest, canonical fused PR artifact, release hard-fail). (path-gated ubuntu selftest, fused PR artifact on the main repo, release
hard-fail).
- File transfer (MTP / SD / FTP): [docs/switch-transfer.md](docs/switch-transfer.md). - File transfer (MTP / SD / FTP): [docs/switch-transfer.md](docs/switch-transfer.md).
## Modding ## Modding
+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 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. 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 ## Prerequisites by OS
All packaging entrypoints are **bash**. On Windows, use Git Bash, MSYS2, or 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 ### macOS / Linux
@@ -25,7 +25,7 @@ WSL — not cmd.exe or PowerShell (AD-008).
sudo dkp-pacman -S switch-dev 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 ```sh
bash scripts/switch/install_devkitpro_deps.sh # native 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 `--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 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: 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 - **WSL** (Ubuntu/etc.) with the Linux pacman flow above, or
- **Git Bash** for `--fetch` / `--loose`; for `--fused` prefer MSYS2 or - **Git Bash** for `--fetch` / `--loose`; for `--fused` prefer MSYS2 or
WSL if Docker bind-mounts from Git Bash paths misbehave. 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. 3. Do **not** expect `scripts/build_switch.sh` to run under cmd/PowerShell.
### What you must install yourself ### What you must install yourself
| You install | Script does **not** install | | 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 …` | | `dkp-pacman` + `switch-dev` + OTA packages **or** Docker | `dkp-pacman -S …` |
| A legal `.gb` ROM (to play) | Any ROM or game data | | A legal `.gb` ROM (to play) | Any ROM or game data |
@@ -96,7 +96,7 @@ See `native/switch-ota-launcher/README.md` and
Rules: Rules:
- `--fetch` alone is fine; combine as `--fetch --loose` or `--fetch --fused`. - `--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). - `--version X.Y.Z` sets the NACP / filename version (defaults to short git SHA).
### What `--fetch` downloads ### 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_*`, `tests/engine/switch_diagnostics_test.lua`, `tests/engine/platform_nx_*`,
or the Switch-related workflow YAML), CI runs: 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/selftest_build_switch.sh`,
`scripts/switch/verify_payload.sh --self-test`, `scripts/switch/verify_payload.sh --self-test`,
`luajit tests/switch_ci_workflows_test.lua`, `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`, headlessly (`luajit tests/engine/assets_version_fallback_test.lua`,
`luajit tests/engine/nx_generated_guard_test.lua`, `luajit tests/engine/nx_generated_guard_test.lua`,
`luajit tests/engine/nx_yellow_boot_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 (`bryanthaboi/gen1recomp`), on the self-hosted Mac runner
(`scripts/build_switch.sh --fetch --fused`), and only when the workflow (`scripts/build_switch.sh --fetch --fused`), and only when the workflow
head is that repo (same-repo push/PR). **Fork repository** CI never runs 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.
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.
3. On successful PR fused builds, a follow-up workflow posts a PR comment 3. On successful PR fused builds, a follow-up workflow posts a PR comment
linking the Actions artifact named `gen1recomp-switch-nro` linking the Actions artifact named `gen1recomp-switch-nro`
(comment tag `switch-build-result`; see (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`) ### Release hard-fail (`.github/workflows/release.yml`)
GitHub Releases always build Switch on the same self-hosted Mac runner as the 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 ```sh
scripts/build_switch.sh --fetch --fused --version "<release version>" scripts/build_switch.sh --fetch --fused --version "<release version>"
@@ -221,7 +217,7 @@ sudo dkp-pacman -S switch-dev
export DEVKITPRO=/opt/devkitpro export DEVKITPRO=/opt/devkitpro
export PATH="$DEVKITPRO/tools/bin:$PATH" export PATH="$DEVKITPRO/tools/bin:$PATH"
# OTA launcher pick one: # OTA launcher: pick one
bash scripts/switch/install_devkitpro_deps.sh # native bash scripts/switch/install_devkitpro_deps.sh # native
# or ensure Docker is installed (same pin as fused builds) # 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) - Push files to the console (no automated MTP / FTP / SD scripting)
- Bundle or download any Pokémon ROM - Bundle or download any Pokémon ROM
- Install `dkp-pacman` / `switch-dev` for you - Install `dkp-pacman` / `switch-dev` for you
- Provide `nxlink` / netloader deploy (deferred see [switch-transfer.md](switch-transfer.md)) - Provide `nxlink` / netloader deploy (deferred; see [switch-transfer.md](switch-transfer.md))
- Validate **Applet Mode** — use title override (hold **R**) for full memory - Validate **Applet Mode**. Use title override (hold **R**) for full memory
Player install steps: [switch-install.md](switch-install.md). Player install steps: [switch-install.md](switch-install.md).
Manual transfer (MTP / SD / FTP, macOS / Linux / Windows): 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: Every GitHub Release that includes Switch support ships an SD-ready zip:
`gen1recomp-*-switch.zip`. Extract it at the root of your microSD (install `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. own legal `.gb` ROM.
> You need a console that can run Switch homebrew (custom firmware / hbmenu). > 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 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 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**. 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) #### Native OTA launcher (in-console)
On Switch, over-the-air updates are handled by the **native OTA launcher** Switch OTA runs in a separate **native launcher NRO** (libnx + curl), not the
(DEVKITPRO / libnx + curl), **not** by the LÖVE self-updater LÖVE self-updater (`src/update/Check.lua`). hbmenu opens `gen1recomp.nro`.
(`src/update/Check.lua`). The hbmenu entry is the launcher NRO
(`gen1recomp.nro`). If a newer release exists it may download the same When a newer release exists, the launcher downloads the same install zip
`gen1recomp-*-switch.zip` used for install, verify SHA-256 from (`gen1recomp-*-switch.zip`), checks SHA-256 against `sha256sums.txt`, replaces
`sha256sums.txt`, replace **both** `gen1recomp-game.nro` and both `gen1recomp-game.nro` and `gen1recomp.nro` (keeps NACP version in sync
`gen1recomp.nro` (so the NACP version matches the release for hbmenu / for hbmenu and Sphaira), then loads the game with `envSetNextLoad`.
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 If you are up to date or offline, it skips straight to the game with no
UI. When an update is available it shows a short screen in the same visual prompt. If an update is available, you get a short prompt styled like the
language as the in-game launcher (black field, RGB rail, logo, A/B buttons). in-game launcher: black background, RGB rail, logo, A/B buttons. Saves under
Saves under `pokemon-love2d/` are never touched. Protocol `pokemon-love2d/` are not touched. See `src/update/SwitchOta.lua` for the
contract: `src/update/SwitchOta.lua`. wire format.
The LÖVE self-updater stays **disabled** on NX (`networkValidated == false`). The LÖVE self-updater stays **disabled** on NX (`networkValidated == false`).
**Sphaira forwarder (HOME shortcut):** Sphaira copies name/version/icon into **Sphaira forwarder (HOME shortcut):** Sphaira copies name/version/icon into
the installed forwarder at creation time. After an OTA (or zip) update, the 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 keeps the old badge until you **reinstall the forwarder once** in Sphaira
(Install Forwarder again on `gen1recomp.nro`). Browsing the NRO in Sphaira / (Install Forwarder again on `gen1recomp.nro`). Browsing the NRO in Sphaira /
hbmenu always shows the live file version. 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 Use the **same** extract/merge of `gen1recomp-*-switch.zip`. It replaces the
NROs (and the small help `README.txt` / `INSTALL.txt` files). Saves, 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. delete that folder** when updating, or you will lose progress.
## 3. Launch with title override ## 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). **Applet Mode is not supported** for this game (not enough memory).
1. On the Switch HOME menu, highlight any installed title. 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). (title override).
3. From hbmenu, open `gen1recomp`. 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 (`.gbc`) dump into `switch/gen1recomp/pokemon-love2d/imports/` (the
launcher also shows the live save-dir path). All three can sit in the launcher also shows the live save-dir path). All three can sit in the
same folder. same folder.
2. Use **Scan again** on that games tab (Red / Blue / Yellow). Rescan 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 matches by ROM SHA-1 for the open tab only. A Red dump never imports
from the Yellow tab (and vice versa). from the Yellow tab (and vice versa).
## 5. Import / Export a raw `.sav` ## 5. Import / Export a raw `.sav`
Continue a cart or PC battery save (or pull a slot off-console) via MTP / 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 | | 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/` | | Blue | `imports/saves/blue/` | `exports/blue/` |
| Yellow | `imports/saves/yellow/` | `exports/yellow/` | | 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)). ([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. **SAVE FILES****Import save**. Only that folder is scanned.
3. A successful import retires the file to `*.sav.imported` and records its 3. A successful import retires the file to `*.sav.imported` and records its
content hash so pressing **Import save** again does not clone slots. content hash so pressing **Import save** again does not clone slots.
Failed imports leave the original `.sav` in place. Failed imports leave the original `.sav` in place.
4. To pull a slot off the console, use **Export save**, then copy the file 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. create `._*.sav` AppleDouble sidecars that are not real saves.
## Controls ## 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): Mods install from a zip inbox (same transfer methods as ROMs):
1. Copy a release `.zip` into the save-dir **`imports/mods/`** path the 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 → 2. In the launcher, open **MODS****Scan again** → enable the mod →
**Play**. **Play**.
Remote **FIND MODS** / GitHub download stays **off** on Switch. Do not put 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. does not document third-party control tables.
### Joy-Con shortcuts (Select + face) ### Joy-Con shortcuts (Select + face)
@@ -185,16 +186,16 @@ If the handheld stutters with extras on, try **OPTIONS → PERFORMANCE** →
## Limitations ## Limitations
- **Homebrew required** — custom firmware and hbmenu; this project does not help - You need homebrew (custom firmware, hbmenu). This project does not set that
set that up. up.
- **Title override required** — hold **R** when launching a title for full - Launch with title override (hold **R** on a title). Applet Mode (Album) is
memory. Applet Mode (Album) is not supported. not supported. The game needs full memory.
- **Manual file transfer** — ROMs, mods, and saves are copied via MTP, direct - ROMs, mods, and saves are copied manually via MTP, direct SD, or FTP. There
SD, or FTP; there is no automated deploy. is no automated deploy.
- **No LÖVE self-updater** — in-console updates use the native OTA launcher - Updates use the native OTA launcher only. The LÖVE self-updater and remote
only. Remote **FIND MODS** / GitHub download stays off on Switch. **FIND MODS** stay off on Switch.
- **Hardware** — tested on Switch OLED; Switch V1 / Erista boot confirmed by the - Tested on Switch OLED. Switch V1 / Erista boot confirmed by the community.
community. Other models may work but are less tested. Other models may work but are less tested.
## Prefer building it yourself? ## Prefer building it yourself?
+17 -17
View File
@@ -1,16 +1,16 @@
# Switch file transfer (MTP / SD / FTP) # Switch file transfer (MTP / SD / FTP)
Canonical ways to put Gen1Recomp artifacts and inbox files onto a Nintendo Ways to put Gen1Recomp artifacts and inbox files onto a Nintendo Switch.
Switch. **Any method is valid** if the bytes land in the destinations below. **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 Player install (what to download, title override) stays in
[switch-install.md](switch-install.md). Packaging stays in [switch-install.md](switch-install.md). Packaging stays in
[switch-build.md](switch-build.md). [switch-build.md](switch-build.md).
> **Not supported yet:** `nxlink` / hbmenu netloader automation. Useful later > **Not supported yet:** `nxlink` / hbmenu netloader automation. Useful later
> for a fast contrib rebuild loop; deferred on purpose (AD-009). Do not treat > for a fast contrib rebuild loop; deferred (AD-009). Do not treat netloader as
> netloader as the release or ROM/mod install path. > 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 | | 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/`) | | 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** | | 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) | | 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` | | Opt-in diagnostics | Empty `switch-debug.txt` in the save dir → `switch.log` |
| Lua error log | `lua-error.log` in the save dir | | 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) ### 1. MTP (DBI responder + host client)
@@ -47,7 +47,7 @@ before launching.
#### macOS (example: OpenMTP) #### macOS (example: OpenMTP)
[OpenMTP](https://github.com/ganeshrvel/openmtp) is a documented example for [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. 1. Quit other MTP clients.
2. Open OpenMTP → select the DBI device → **`1: SD Card`**. 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. 5. Wait for the queue; refresh; exit MTP responder; title-override launch.
macOS clients often create AppleDouble sidecars (`._Something.zip`, 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` launcher skips hidden `.*` names. Delete `._*` junk if a zip/ROM/`.sav`
fails to open. fails to open.
#### Linux #### Linux
1. Install desktop MTP support if needed (e.g. `gvfs-mtp` on GNOME/GTK 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 2. With DBI MTP active, open **Files** / **Dolphin** / **Thunar** and select
the Switch / DBI device → **`1: SD Card`**. the Switch / DBI device → **`1: SD Card`**.
3. Extract the release zip at SD root (merge), or copy into `switch/gen1recomp/` 3. Extract the release zip at SD root (merge), or copy into `switch/gen1recomp/`
and the save-dir inboxes as above. and the save-dir inboxes as above.
4. Use **only one** MTP accessor at a time. If `mtp-tools` / `mtpfs` reports 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. and retry with a single client.
5. Eject/unmount cleanly; exit MTP on the Switch; title-override launch. 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 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 #### 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. 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 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) ### 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) ### 3. FTP (any SD-exposing Switch FTP)
Any homebrew FTP server that can write the microSD is fine — for example Any homebrew FTP server that can write the microSD is fine. For example
**DBIs own FTP**, **sys-ftpd-light**, or **Sphaira** (names are illustrations **DBI's own FTP**, **sys-ftpd-light**, or **Sphaira**. Names are illustrations
only; pick what your CFW setup already uses). only; pick what your CFW setup already uses.
1. Start the FTP server on the Switch; note IP/port/credentials from that app. 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 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 | | 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) | | 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 | | 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 | | 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. checkout is always "the game" itself; updating it means pulling the repo.
- **Nintendo Switch does not use this LÖVE self-updater.** On NX, - **Nintendo Switch does not use this LÖVE self-updater.** On NX,
`Platform.networkValidated()` is `false`, so `Boot.run` / `Check` never `Platform.networkValidated()` is `false`, so `Boot.run` / `Check` never
download `.love` payloads. In-console OTA is the **native OTA launcher** download `.love` payloads. In-console OTA uses the **native OTA launcher**
(DEVKITPRO) documented in [switch-install.md](switch-install.md); protocol contract (DEVKITPRO), documented in [switch-install.md](switch-install.md). Wire
`src/update/SwitchOta.lua`. Manual zip install remains the fallback. format: `src/update/SwitchOta.lua`. Manual zip install remains the fallback.
+9 -9
View File
@@ -6,20 +6,20 @@ are already up to date or offline). Only if a newer release exists does it show
a **launcher-style screen** (black + RGB rail + project logo + flat A/B a **launcher-style screen** (black + RGB rail + project logo + flat A/B
buttons), download the same `gen1recomp-*-switch.zip` used for install, verify buttons), download the same `gen1recomp-*-switch.zip` used for install, verify
SHA-256 from `sha256sums.txt`, replace **both** `gen1recomp-game.nro` and SHA-256 from `sha256sums.txt`, replace **both** `gen1recomp-game.nro` and
`gen1recomp.nro` (matching NACP version for hbmenu/Sphaira), then hand off `gen1recomp.nro` (matching NACP version for hbmenu/Sphaira), then load the game
with `envSetNextLoad`. with `envSetNextLoad`.
The LÖVE self-updater (`src/update/Check.lua`) stays **disabled** on NX. The LÖVE self-updater (`src/update/Check.lua`) stays **disabled** on NX.
Protocol contract (also in Lua): `src/update/SwitchOta.lua`. Wire format (also in Lua): `src/update/SwitchOta.lua`.
NACP icon: `assets/switch/icon.jpg`. NACP icon: `assets/switch/icon.jpg`.
## Layout on microSD ## Layout on microSD
```text ```text
sdmc:/switch/gen1recomp/gen1recomp.nro this launcher sdmc:/switch/gen1recomp/gen1recomp.nro <- this launcher
sdmc:/switch/gen1recomp/gen1recomp-game.nro fused LÖVE game sdmc:/switch/gen1recomp/gen1recomp-game.nro <- fused LÖVE game
sdmc:/switch/gen1recomp/version.txt installed X.Y.Z sdmc:/switch/gen1recomp/version.txt <- installed X.Y.Z
sdmc:/switch/gen1recomp/pokemon-love2d/ saves (never touched by OTA) sdmc:/switch/gen1recomp/pokemon-love2d/ <- saves (never touched by OTA)
``` ```
## Host tests (no DEVKITPRO) ## Host tests (no DEVKITPRO)
@@ -42,7 +42,7 @@ Needs `DEVKITPRO` with packages roughly:
export DEVKITPRO=/opt/devkitpro # typical export DEVKITPRO=/opt/devkitpro # typical
cd native/switch-ota-launcher cd native/switch-ota-launcher
make make
# gen1recomp.nro # -> gen1recomp.nro
``` ```
Or from repo root (as part of `--fused`): Or from repo root (as part of `--fused`):
@@ -62,12 +62,12 @@ Docker fallback uses the same pin as fused builds (`scripts/switch/dkp-docker.im
## Packaging ## Packaging
`scripts/switch/pack_sd_zip.sh GAME_NRO VERSION OUT_ZIP LAUNCHER_NRO` writes both `scripts/switch/pack_sd_zip.sh GAME_NRO VERSION OUT_ZIP LAUNCHER_NRO` writes both
NROs into the SD zip. That zip is also the OTA download asset (unified). NROs into the SD zip. That zip is also what OTA downloads.
Manifest: `scripts/switch/ota_launcher.manifest`. Manifest: `scripts/switch/ota_launcher.manifest`.
## Status / known gaps ## Status / known gaps
- Zip extraction uses `switch-zziplib` (`ota_unzip.c`) on device. - Zip extraction uses `switch-zziplib` (`ota_unzip.c`) on device.
- OTA replaces launcher + game from the unified zip (NACP versions stay aligned). - OTA replaces launcher + game from the install zip (NACP versions stay aligned).
- Sphaira HOME forwarders cache metadata until reinstalled (see docs/switch-install.md). - Sphaira HOME forwarders cache metadata until reinstalled (see docs/switch-install.md).
- Release runner: `switch-dev` + (`install_devkitpro_deps.sh` **or** Docker) - Release runner: `switch-dev` + (`install_devkitpro_deps.sh` **or** Docker)
+1 -1
View File
@@ -4,7 +4,7 @@
# entrypoint. The LÖVE fused game NRO is loaded via envSetNextLoad after the # entrypoint. The LÖVE fused game NRO is loaded via envSetNextLoad after the
# update check. Self-update of the launcher itself is out of scope for v1. # update check. Self-update of the launcher itself is out of scope for v1.
# #
# OTA download uses the same SD zip as install (unified asset). # OTA downloads the install zip.
# #
# Paths relative to microSD root: # Paths relative to microSD root:
+2 -2
View File
@@ -410,7 +410,7 @@ if [ ! -f "$LEGACY_OTA_PACKER" ] \
&& ! grep -Eq 'pack_ota_zip\.sh|switch-ota\.zip' "$ROOT/scripts/build_switch.sh" \ && ! grep -Eq 'pack_ota_zip\.sh|switch-ota\.zip' "$ROOT/scripts/build_switch.sh" \
&& grep -q 'OTA_ASSET_GLOB=gen1recomp-\*-switch.zip' "$MANIFEST" && grep -q 'OTA_ASSET_GLOB=gen1recomp-\*-switch.zip' "$MANIFEST"
then then
ok "unified OTA download is the same SD zip (legacy OTA-only packer gone)" ok "OTA uses the same SD zip as install (legacy OTA-only packer gone)"
else else
bad "legacy separate OTA-only packer / asset still present" bad "legacy separate OTA-only packer / asset still present"
fi fi
@@ -419,7 +419,7 @@ if grep -q 'ota_ui_prompt_update' "$ROOT/native/switch-ota-launcher/src/main.c"
&& grep -q 'framebufferCreate\|COL_RAIL' "$ROOT/native/switch-ota-launcher/src/ota_ui.c" \ && grep -q 'framebufferCreate\|COL_RAIL' "$ROOT/native/switch-ota-launcher/src/ota_ui.c" \
&& grep -q '^ROMFS' "$ROOT/native/switch-ota-launcher/Makefile" && grep -q '^ROMFS' "$ROOT/native/switch-ota-launcher/Makefile"
then then
ok "launcher UI is branded framebuffer (quiet by default)" ok "launcher UI uses framebuffer (no prompt when up to date)"
else else
bad "launcher missing branded ota_ui / ROMFS" bad "launcher missing branded ota_ui / ROMFS"
fi fi
+1 -1
View File
@@ -1,4 +1,4 @@
-- Switch OTA protocol contract (host-testable, no love.*). -- Switch OTA wire format (host-testable, no love.*).
-- The native DEVKITPRO launcher (libnx + switch-curl) must implement the -- The native DEVKITPRO launcher (libnx + switch-curl) must implement the
-- same decisions. LÖVE on NX never runs this path — Platform.networkValidated -- same decisions. LÖVE on NX never runs this path — Platform.networkValidated
-- stays false and src/update/Check.lua remains gated off on NX. -- stays false and src/update/Check.lua remains gated off on NX.
+2 -2
View File
@@ -571,8 +571,8 @@ test('AC-011: Empacotamento dual-NRO e selftest @spec:AC-011', () => {
const selftest = read('scripts/switch/selftest_build_switch.sh'); const selftest = read('scripts/switch/selftest_build_switch.sh');
assert.match(selftest, /dual-NRO|gen1recomp-game\.nro/); assert.match(selftest, /dual-NRO|gen1recomp-game\.nro/);
assert.match(selftest, /ota_launcher\.manifest/); assert.match(selftest, /ota_launcher\.manifest/);
assert.match(selftest, /unified OTA|same SD zip|legacy OTA-only/i); assert.match(selftest, /OTA uses the same SD zip|same SD zip|legacy OTA-only/i);
assert.match(selftest, /ota_ui|branded framebuffer|Quiet by default|quiet by default/i); assert.match(selftest, /ota_ui|framebuffer|no prompt when up to date/i);
const manifest = read('scripts/switch/ota_launcher.manifest'); const manifest = read('scripts/switch/ota_launcher.manifest');
assert.match(manifest, /^OTA_ENABLED=1$/m); assert.match(manifest, /^OTA_ENABLED=1$/m);
+2 -2
View File
@@ -157,14 +157,14 @@ local readme = read("README.md")
mustContain(build_doc, "Path-gated", "switch-build.md") mustContain(build_doc, "Path-gated", "switch-build.md")
mustContain(build_doc, "ubuntu-latest", "switch-build.md") mustContain(build_doc, "ubuntu-latest", "switch-build.md")
mustContain(build_doc, "selftest_build_switch.sh", "switch-build.md") mustContain(build_doc, "selftest_build_switch.sh", "switch-build.md")
mustContain(build_doc, "canonical", "switch-build.md") mustContain(build_doc, "main repo", "switch-build.md")
mustContain(build_doc, "gen1recomp-switch-nro", "switch-build.md") mustContain(build_doc, "gen1recomp-switch-nro", "switch-build.md")
mustContain(build_doc, "switch-build-result", "switch-build.md") mustContain(build_doc, "switch-build-result", "switch-build.md")
mustContain(build_doc, "hard gate", "switch-build.md") mustContain(build_doc, "hard gate", "switch-build.md")
mustContain(build_doc, "continue-on-error", "switch-build.md") mustContain(build_doc, "continue-on-error", "switch-build.md")
mustContain(build_doc, "nacptool", "switch-build.md") mustContain(build_doc, "nacptool", "switch-build.md")
mustContain(build_doc, "Docker", "switch-build.md") mustContain(build_doc, "Docker", "switch-build.md")
mustContain(build_doc, "Fork → canonical", "switch-build.md") mustContain(build_doc, "Fork PRs into the main repo", "switch-build.md")
mustContain(build_doc, "skip Switch fused", "switch-build.md") mustContain(build_doc, "skip Switch fused", "switch-build.md")
mustContain(build_doc, "CI and release", "switch-build.md") mustContain(build_doc, "CI and release", "switch-build.md")
mustNotContain(build_doc, "switch-development", "switch-build.md") mustNotContain(build_doc, "switch-development", "switch-build.md")
+3 -3
View File
@@ -32,7 +32,7 @@ mustContain(transfer, "imports/", "transfer")
mustContain(transfer, "imports/mods/", "transfer") mustContain(transfer, "imports/mods/", "transfer")
mustContain(transfer, "1: SD Card", "transfer") mustContain(transfer, "1: SD Card", "transfer")
mustContain(transfer, "Scan again", "transfer") mustContain(transfer, "Scan again", "transfer")
mustContain(transfer, "one contributor workflow", "transfer") mustContain(transfer, "documented example", "transfer")
mustContain(transfer, "Linux", "transfer") mustContain(transfer, "Linux", "transfer")
mustContain(transfer, "Windows", "transfer") mustContain(transfer, "Windows", "transfer")
mustContain(transfer, "macOS", "transfer") mustContain(transfer, "macOS", "transfer")
@@ -46,7 +46,7 @@ mustContain(transfer, "Portable Devices", "transfer")
mustContain(transfer, "MTP USB Device", "transfer") mustContain(transfer, "MTP USB Device", "transfer")
mustContain(transfer, "AppleDouble", "transfer") mustContain(transfer, "AppleDouble", "transfer")
mustContain(transfer, "card reader", "transfer") mustContain(transfer, "card reader", "transfer")
mustContain(transfer, "Canonical methods", "transfer") mustContain(transfer, "Transfer methods", "transfer")
mustContain(transfer, "OpenMTP", "transfer") mustContain(transfer, "OpenMTP", "transfer")
mustContain(transfer, "only one", "transfer") mustContain(transfer, "only one", "transfer")
mustContain(transfer, "USB-C", "transfer") mustContain(transfer, "USB-C", "transfer")
@@ -69,7 +69,7 @@ mustContain(install, "GBC FX", "install")
mustContain(install, "PERFORMANCE", "install") mustContain(install, "PERFORMANCE", "install")
mustContain(install, "Stock engine effect", "install") mustContain(install, "Stock engine effect", "install")
mustContain(install, "## Limitations", "install") mustContain(install, "## Limitations", "install")
mustContain(install, "Title override required", "install") mustContain(install, "Launch with title override", "install")
mustNotContain(install, "VoxelMod", "install") mustNotContain(install, "VoxelMod", "install")
mustNotContain(install, "switch-development", "install") mustNotContain(install, "switch-development", "install")
mustContain(build, "switch-transfer.md", "build") mustContain(build, "switch-transfer.md", "build")
+3 -3
View File
@@ -1,4 +1,4 @@
# switch-probe love-nx hardware probe # switch-probe: love-nx hardware probe
**NOT FOR RELEASE.** This package is a developer-only diagnostic for Nintendo Switch (love-nx). Do not ship it inside `game.love` or release NRO payloads. **NOT FOR RELEASE.** This package is a developer-only diagnostic for Nintendo Switch (love-nx). Do not ship it inside `game.love` or release NRO payloads.
@@ -12,7 +12,7 @@ Validate Phase 0 runtime facts on real Switch hardware before running the full G
- Gamepad / joystick / touch event logging - Gamepad / joystick / touch event logging
To date this probe has only been run on **Switch OLED**; other models are To date this probe has only been run on **Switch OLED**; other models are
untested. Deploy beside `gen1recomp.nro` remains **manual** (MTP); see untested. Deploy beside `gen1recomp.nro` remains **manual** (MTP). See
`docs/switch-transfer.md`. `docs/switch-transfer.md`.
## Fields shown on screen ## Fields shown on screen
@@ -39,7 +39,7 @@ zip -9 -j .bazinga/work/switch-probe.love tools/switch-probe/main.lua tools/swit
``` ```
Deploy beside `gen1recomp.nro` (loose mode) per `docs/switch-build.md` and Deploy beside `gen1recomp.nro` (loose mode) per `docs/switch-build.md` and
`docs/switch-transfer.md`, renaming to `game.love` only for a probe run — use a `docs/switch-transfer.md`. Rename to `game.love` only for a probe run. Use a
separate SD folder so probe and game builds do not mix. separate SD folder so probe and game builds do not mix.
## Desktop smoke (optional) ## Desktop smoke (optional)