A command-line utility that converts classic ROMs (NES/SNES/Genesis/…) into installable Nintendo Switch titles.
- Each generated NRO wraps a single ROM using a tiny libnx stub.
- On first launch the stub copies the ROM to
/roms/<platform>/on SD. - Icons are auto-fetched from the Libretro Thumbnails repo
(with configurable preference: logos by default, or boxarts). - If no match is found, the packer falls back to a generated initials-based JPEG.
- NSP forwarder build support is now wired up — the project vendors hacBrewPack as a submodule, and the pipeline can produce per-ROM NSP forwarders.
- Current limitation: produced NSPs install but show as a grey box with a spinner on hardware. This will be addressed in the next milestone.
- Designed to batch through large libraries with minimal config.
Project status: Early WIP (Sep 2025). Working libnx stub + Python packer pipeline + icon auto-fetch + NSP forwarder build flow (using hacBrewPack).
NSPs are being built end-to-end, but forwarder boot logic isn’t yet functional.
- devkitPro + libnx installed and on
PATH. - Python 3.10+ for the packer.
- Pillow (installed via requirements.txt) for icon conversion/fallback generation.
- A working Makefile in
stub/(provided) that accepts:APP_TITLE,APP_AUTHOR,APP_VERSION,ICONROMFSfolder contents (autopopulated by the packer)
- hacBrewPack for NSP builds (already included as a submodule).
To build tools/hacbrewpack/, you need a C toolchain and supporting build tools.
On Ubuntu / Debian (including WSL):
sudo apt-get update
sudo apt-get install -y build-essential cmake pkg-config zlib1g-devThese provide gcc, make, and headers needed to compile mbedtls (used by hacBrewPack).
When cloning this repo, make sure to pull submodules so tools/hacbrewpack/ is available:
git clone --recurse-submodules https://github.com/HarleyBartles/switch-rom-packerIf you already cloned without submodules:
git submodule update --init --recursive# Example:
python3 packer.py ~/rom_input/Notes:
rom_rootis the positional first argument (e.g.~/rom_input/).--build-nrois on by default; you don’t need to pass it.--stub-dir,--output-dir, and--filelist-outall have sensible defaults.- The packer writes a
filelist.txtinto the stub’s RomFS (platform + filename) which the stub reads to copy the ROM to/roms/<platform>/<romfile>at first launch. - The icon pipeline:
- Looks up
Named_Logos,Named_Boxarts,Named_Titles, thenNamed_Snapsin that order (logos first). - Can be overridden with
--icon-preference boxarts.
- Looks up
-
packer.py discovers ROMs under
rom_root, infers platform (simple rules for now), then:- Generates a per-ROM stub RomFS with a
filelist.txt. - Calls the stub Makefile to produce a per-ROM NRO with dynamic title and icon.
- Icons are auto-fetched from Libretro thumbnails, cached under
~/.switch-rom-packer/cache/icons/. - Writes outputs to the chosen
--output-dir.
- Generates a per-ROM stub RomFS with a
-
stub/ (libnx) boots, reads
filelist.txt, and performs a one-time copy to the SD card. -
forwarder/ (libnx) builds exefs/main + main.npdm via its Makefile, which is installed into
stub/vendor/exefs/for use in NSP forwarders. -
NSP build integration (working, but limited)
tools/hacbrewpack/is included as a submodule.- Pipeline now stages RomFS/ExeFS/Control/Logo and invokes hacBrewPack.
- NSPs are successfully produced, but forwarders currently show as a grey spinner when launched on real hardware.
It’s recommended to use a Python virtual environment to keep dependencies isolated.
# create a venv in the project root (only once)
python3 -m venv .venv
# activate it
# on Linux/macOS:
source .venv/bin/activate
# on Windows (PowerShell):
.venv\Scripts\Activate.ps1
# install dependencies into the venv
pip install -r requirements.txtusage: packer.py [-h] [--build-nro/--no-build-nro] [--build-nsp/--no-build-nsp]
[--stub-dir STUB_DIR] [--output-dir OUTPUT_DIR]
[--filelist-out FILELIST_OUT]
[--keys KEYS] [--forwarder {retroarch,nro}]
[--core-map CORE_MAP] [--titleid-base TITLEID_BASE]
[--icon-preference {logos, boxarts}] [--debug-icons]
rom_root
rom_root(positional): directory containing your ROMs.--build-nro(default enabled): produce NROs via the stub Makefile.--no-build-nro: disable NRO output.--build-nsp(default enabled): produce NSP forwarders via hacBrewPack.--no-build-nsp: disable NSP output.--stub-dir: path to the libnx stub (default:./stub).--output-dir: directory for generated outputs (default:./out).--filelist-out: where to write the stub’sfilelist.txt.--keys: path toprod.keysfor hacBrewPack (default:~/.switch/prod.keys).--forwarder: forwarder mode (retroarchlaunches RetroArch core,nrojumps to arbitrary NRO).--core-map: YAML file mapping<platform> -> <core nro path>.--titleid-base: optional deterministic TitleID salt (16 hex).--icon-preference(options [logos,boxarts], defaultlogos): choose thumbnail set priority.--debug-icons: enable additional logging during icon lookup.
- The Makefile is wired so the packer can set
APP_TITLE/APP_AUTHOR/APP_VERSION/ICONand populate RomFS for each ROM build automatically. - Icon cache is stored under
~/.switch-rom-packer/cache/icons/. tools/hacbrewpack/is vendored as a submodule (pinned release). Submodule changes are ignored at the parent repo level.- Forwarder exefs build is now automated via
make installinforwarder/. - NSP build pipeline is wired up and tested, but forwarder behavior needs debugging.
- libnx stub that reads
filelist.txtand copies to/roms/<platform>/<romfile>. - Python packer that:
- Takes
rom_rootas positional arg. - Defaults
--build-nroto on. - Provides sensible defaults for
--stub-dir,--output-dir,--filelist-out. - Integrates with the Makefile (RomFS auto-population, dynamic titles, icons).
- Fetches and caches Libretro thumbnails (logos/boxarts) with fallback initials icons.
- hacBrewPack vendored as
tools/hacbrewpack/submodule, wired into NSP build flow. - forwarder/ project builds exefs/main + main.npdm and installs into stub/vendor/exefs.
- Takes
-
NSP forwarder runtime debugging
- Fix why NSPs install but only display a grey spinner on hardware.
- Ensure RetroArch forwarder logic launches correctly.
-
Platform detection & metadata
- Expand platform inference rules (by folder name and filename suffixes).
- Optional manifest file to pin platform/core when heuristics are ambiguous.
-
RetroArch core mapping
- Maintain a simple map:
<platform> -> <core name>. - Allow per-title overrides.
- Maintain a simple map:
-
Quality & DX
- Dry-run mode, better logging, progress bars for big batches.
- Structured outputs + summary table at the end (counts, failures).
- CI checks (format, lint, stub compiles).
- Optional content-hash de-duplication (skip rebuild if ROM+settings unchanged).
- Simple HTML report of a batch (titles, platforms, icon hits/misses).
- Pluggable “icon providers” (try multiple FOSS sources in order).
- Live scraping of non-FOSS or rate-limited sources.
- Multi-ROM compilations in a single title (e.g., collections).
- NSP forwarders currently install but do not boot properly (grey spinner).
- Platform detection is heuristic and may need manual overrides for edge cases.
Q: Can I just run python3 packer.py ~/rom_input/ without extra flags?
Yes. That’s the intended default path: it builds NROs and NSPs using sensible defaults.
Q: Where do ROMs end up on the SD card?
On first run the stub copies them to /roms/<platform>/<romfile>.
Q: Will these show up as installable titles on HOME?
Yes — the NSP build pipeline produces installable forwarders, but they don’t yet boot correctly.