Skip to content

Latest commit

 

History

History
228 lines (163 loc) · 8.56 KB

File metadata and controls

228 lines (163 loc) · 8.56 KB

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