nsz-rs
Lossless zstd compression of Nintendo Switch NSP dumps into NSZ, and back, in
Rust. Part of oxyROMon, as a library and
the nszrs CLI. Its NSZs are interchangeable with those of
nsz, which it began as a port of (see
Credits).
Status
| Feature | Supported |
|---|---|
| NSZ → NSP decompression | yes |
| NSP → NSZ, solid | yes |
| NSP → NSZ, block (parallel) | yes |
| XCI ↔ XCZ | no |
Titlekeys from .tik in the NSP |
yes |
| BKTR (update) section splitting | yes |
| SHA-256 verification vs. CNMT | yes |
NCZ files produced by nsz-rs use the same on-disk format as nsz and can be
decompressed by either tool. As in nsz, only Program and PublicData NCAs whose
sections tile the file are compressed; everything else is copied verbatim.
Both directions stream one member at a time, so memory use doesn't grow with
the dump size. The output is written to <output>.part and renamed into
place once complete, so a failed run leaves an existing output alone.
Compressing a rights-managed NCA without its ticket (or a title.keys entry)
is an error rather than a silent uncompressed copy.
CLI
nszrs compresses NSPs and decompresses NSZs, telling which from each file's
extension, and writes next to each one unless told otherwise:
cargo build --release -p nsz-rs
nszrs game.nsz # decompress, next to it
nszrs --long-distance -o out/ *.nsp # solid compress, long-distance matching
nszrs -b 1048576 -o out/ game.nsp # compress in 1 MiB blocks, in parallel
| Flag | Meaning |
|---|---|
-o DIR |
output directory (default: next to each input) |
-l N |
zstd level (default 18) |
-b SIZE |
compress in independent blocks of SIZE bytes, a power of two in 16384..=4294967296, instead of one solid stream |
--long-distance |
zstd long-distance matching |
--fix-padding |
re-pad the PFS0 header to 0x20 alignment |
-k PATH |
prod.keys (default ~/.switch/prod.keys) |
--skip-key-check |
skip the CRC32 check of known keys |
Decompression verifies every NCA against the CNMT and fails on a mismatch. Like
every oxyROMon tool, nszrs draws oxyROMon's progress bar, reports each input
on a line of its own, and exits non-zero when any of them failed. It refuses to write
one input's output over another input, or over an earlier input's output.
Library
The CLI and its progress bar sit behind the default cli feature; embed the
library without them:
= { = "0.3", = false }
use Keys;
use ;
// Keys are passed as a loader, called only if the container needs them.
let keys = ;
let solid = Compression ;
// The last argument receives input bytes consumed; the calls add up to the
// input file size, ready to feed a progress bar.
compress_nsp?;
// keys, fix_padding, verify, strict, progress
let report = decompress_nsz?;
You need your own prod.keys dumped from your own console, but only to
compress NCAs or to verify against a CNMT: containers without NCAs (homebrew,
for instance) and unverified decompression never load it.
Credits
nsz-rs began as a port of nsz by Nico Bosshard, which itself builds on NUT by Blake Warner. The NCZ format, the key derivation and the container handling all come from their work.
License
MIT, see LICENSE. The original nsz copyright notice is retained as the license requires.