Install pipeline

What happens after CyberFoil has a byte stream - container parse, NCA write, tickets, metadata. Transport details: Network.

Overview

All sources converge on the same install orchestrator (install.cpp):

  1. Prepare - read CNMT NCAs, install content meta to NCM DB, push application record to NS
  2. Begin - import tickets/certs via ES, stream each content NCA into storage

.nsz / .xcz are not decompressed as whole files - they use the same PFS0/HFS0 layout with .ncz entries instead of .nca.

install.cpp, install_nsp.cpp, install_xci.cpp

NSP / NSZ - PFS0 container

  • Magic PFS0 (0x30534650)
  • File table: PFS0BaseHeader + entries + string table
  • NCA entries: {id}.nca, {id}.cnmt.nca, {id}.ncz, {id}.cnmt.ncz
  • Ticket pairs: .tik + .certesImportTicket

Streaming (HTTP / USB)

Dual-thread pipeline in http_nsp.cpp / usb_nsp.cpp:

  • Thread 1: HTTP/USB range read → BufferedPlaceholderWriter
  • Thread 2: flush 8 MB segments → NcaWriter → NCM placeholder
  • Ring buffer: 8 MB × 128 segments (2 in applet mode)

Per-NCA steps (NSPInstall::InstallNCA)

  1. Delete stale placeholder
  2. Optional RSA-PSS signature verify (validateNCAs)
  3. Stream to placeholder via NcaWriter
  4. Register placeholder → final NCA id
  5. Delete placeholder; rollback on error

nsp.cpp, install_nsp.cpp, http_nsp.cpp, buffered_placeholder_writer.cpp

XCI / XCZ - HFS0 container

  • Root HFS0 at offset 0xF000 or 0x10000, magic HFS0 (0x30534648)
  • secure partition holds NCAs, tickets, certs
  • Same Prepare/Begin flow as NSP but reads HFS0 entries

Streaming differences

  • HTTP XCI: 4 MB chunks, NcaWriter direct (no ring buffer) - http_xci.cpp
  • SD/USB XCI: same as local NSP pattern via sdmc_xci.cpp

xci.cpp, install_xci.cpp, http_xci.cpp

NCZ decompression (.nsz / .xcz inner NCAs)

Handled inside NcaWriter / nca_writer.cpp while streaming:

MagicFormat
NCZSECTNSectioned NCZ - ZSTD or NCZBLOCK per section
NCZBLOCKBlock-compressed ZSTD stream
0xFD2FB528Raw ZSTD stream
(none)Plain NCA - direct body write

After decompress, section data is re-encrypted (AES-128-CTR) and written to the NCM placeholder in 8 MB flushes.

nca_writer.cpp

NCM placeholder API

Wrapper around Switch ncmContentStorage* in ncm.cpp:

MethodAPI
CreatePlaceholderncmContentStorageCreatePlaceHolder
WritePlaceholderncmContentStorageWritePlaceHolder
RegisterncmContentStorageRegister - finalize NCA
DeletePlaceholderCleanup incomplete install
Content metancmContentMetaDatabase Set + Commit
ApplicationnsPushApplicationRecord

ncm.cpp, nx/ncm.cpp

NCA signature verification

When validateNCAs is true (default in config):

  • Decrypt NCA header (AES-XTS with headerKey)
  • Check magic NCA3
  • RSA-2048-PSS verify first 0x200 bytes of header
  • On failure: warning dialog - user can abort or continue

install_nsp.cpp, optionsPage.cpp (validateNCAs)

Shop XCI - special HTTP stream

Shop XCI items bypass the generic XCIInstallTask and use InstallXciHttpStream in shopInstall.cpp:

  • Detect XCI: extension .xci/.xcz or HFS0 magic at 0xF000/0x10000
  • Parse HFS0 via HTTP range with 16 MB read-ahead cache
  • Write NCAs through NcaWriter, commit CNMT incrementally
  • Import tickets at end - single continuous HTTP connection

NSP/NSZ shop items use standard HTTPNSP + NSPInstall.

shopInstall.cpp - installTitleShop, InstallXciHttpStream