Skip to content

HarleyBartles/switch-rom-packer

Retro ROM → Switch NSP Packer

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.


Quick start

Requirements

  • 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, ICON
    • ROMFS folder contents (autopopulated by the packer)
  • hacBrewPack for NSP builds (already included as a submodule).

Additional system dependencies (for hacBrewPack)

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-dev

These provide gcc, make, and headers needed to compile mbedtls (used by hacBrewPack).


Cloning with submodules

When cloning this repo, make sure to pull submodules so tools/hacbrewpack/ is available:

git clone --recurse-submodules https://github.com/HarleyBartles/switch-rom-packer

If you already cloned without submodules:

git submodule update --init --recursive

Build a batch of NROs

# Example:
python3 packer.py ~/rom_input/

Notes:

  • rom_root is the positional first argument (e.g. ~/rom_input/).
  • --build-nro is on by default; you don’t need to pass it.
  • --stub-dir, --output-dir, and --filelist-out all have sensible defaults.
  • The packer writes a filelist.txt into 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, then Named_Snaps in that order (logos first).
    • Can be overridden with --icon-preference boxarts.

How it works

  1. 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.
  2. stub/ (libnx) boots, reads filelist.txt, and performs a one-time copy to the SD card.

  3. forwarder/ (libnx) builds exefs/main + main.npdm via its Makefile, which is installed into stub/vendor/exefs/ for use in NSP forwarders.

  4. 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.

Setup

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.txt

Usage (packer.py)

usage: 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’s filelist.txt.
  • --keys: path to prod.keys for hacBrewPack (default: ~/.switch/prod.keys).
  • --forwarder: forwarder mode (retroarch launches RetroArch core, nro jumps 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], default logos): choose thumbnail set priority.
  • --debug-icons: enable additional logging during icon lookup.

Development

  • The Makefile is wired so the packer can set APP_TITLE/APP_AUTHOR/APP_VERSION/ICON and 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 install in forwarder/.
  • NSP build pipeline is wired up and tested, but forwarder behavior needs debugging.

Roadmap

Done / baseline

  • libnx stub that reads filelist.txt and copies to /roms/<platform>/<romfile>.
  • Python packer that:
    • Takes rom_root as positional arg.
    • Defaults --build-nro to 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.

In progress / next up

  1. NSP forwarder runtime debugging

    • Fix why NSPs install but only display a grey spinner on hardware.
    • Ensure RetroArch forwarder logic launches correctly.
  2. Platform detection & metadata

    • Expand platform inference rules (by folder name and filename suffixes).
    • Optional manifest file to pin platform/core when heuristics are ambiguous.
  3. RetroArch core mapping

    • Maintain a simple map: <platform> -> <core name>.
    • Allow per-title overrides.
  4. 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).

Nice-to-haves

  • 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).

Out of scope (for now)

  • Live scraping of non-FOSS or rate-limited sources.
  • Multi-ROM compilations in a single title (e.g., collections).

Known limitations

  • NSP forwarders currently install but do not boot properly (grey spinner).
  • Platform detection is heuristic and may need manual overrides for edge cases.

FAQ

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.


License

MIT

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors