Files
2026-10-29 03:55:40 +00:00

9.4 KiB
Raw Permalink Blame History

Switchnote .sppm format

.sppm is Switchnote Studio's own offline animation format. It is not a Nintendo .ppm file. All integers are unsigned little-endian unless stated otherwise. Readers must check every length before allocating or advancing. include/sppm.hpp and source/sppm.cpp are the reference implementation.

The Fakenote Studio repository supplies 1280×720 UI mockups and 640×360 sample artwork, with editor buttons for layers A through D. It does not specify a binary format or an exact ink palette. The payload uses a 640×360 canvas, four indexed layers, and 256 editable RGBA palette entries. Entry 0 is transparent. The four layers composite D, C, B, A from back to front; pattern brushes write ordinary palette indices in their chosen pattern.

Header

Offset Size Value
0 4 ASCII SPPM
4 2 Payload version 2 (1 remains readable)
6 2 Header size 36
8 8 Total file byte length
16 4 CRC-32/IEEE of bytes from offset 36 to end
20 2 Canvas width 640
22 2 Canvas height 360
24 1 Layer count 4
25 2 Palette count 256
27 1 Playback FPS, 1–30
28 1 Flags: bit 0 locked, bit 1 allows spin-offs, bit 2 BGM follows animation speed, bit 3 stops after the final frame (clear = loop)
29 4 Frame count, 1–9999
33 1 Audio track count, 0–4
34 2 Reserved zero

Maximum file size is 512 MiB. The CRC protects against accidental damage; it is not an authentication tag.

Legacy signed v2 envelope

Legacy files may use the binary/RLE payload with the header version set to 2, followed by the four-byte ASCII marker SPPS and a 32-byte HMAC-SHA-256 tag. The tag covers the complete v2 payload, including its header, metadata, frames and audio. Readers verify the tag in constant time before decoding the payload. The HMAC key is retained in an encrypted, console-bound identity-v2-*.bin record. This is a legacy local-file integrity scheme; current app saves use the publicly verifiable Ed25519-signed v3 envelope below. Neither scheme is Nintendo's signature system or proof of a real-world creator identity. Readers accept both old v1 and current v2 payloads inside an authenticated envelope. .sppm is a compact binary encoding with RLE layers, not plaintext and not an encrypted format.

Public v3 signatures

Gallery files use the same SPPM payload with its marker set to version 3, followed by SPP3, public author/original-author/editor identities, and a 64-byte Ed25519 signature. The signed digest covers the complete payload and all three identities. The server validates the signature and checks each available FSID against its included public identity. This is self-certifying: it proves that the included editor key signed those bytes, not that the key belongs to a Nintendo account or a verified human.

The Ed25519 secret is encrypted at rest with XSalsa20-Poly1305 and a wrapping key derived from system-specific console material. On CFW, this material is available to homebrew and is not a hardware-backed, non-exportable secret; a compromised console can still expose its signing key. The protection is intended to make a copied SD identity file unusable on an ordinary different console, not to resist a compromised device.

Palette and metadata

Immediately after the header are 256 RGBA entries, each four bytes in R, G, B, A order. The next five fields are UTF-8 strings with u16 byte length then that many bytes: title (at most 256 bytes), description (4096), author name (128), original author name (128), and last editor name (128). NUL bytes are forbidden. The reader rejects malformed UTF-8 and preserves valid bytes; the frontend is responsible for display and editing.

The default drawing palette uses pink at index 6 and purple at index 10. When reading older SPPMs, the exact opaque legacy colors (92,224,36) and (26,40,72) are remapped to (239,68,133) and (132,66,183). Palette indices are unchanged; saving writes the migrated colors back into the SPPM.

Six IDs follow, each exactly 16 uppercase hexadecimal ASCII characters:

  1. Author FSID, the current Switchnote creator.
  2. Original author FSID, retained through all spin-offs.
  3. Last editor FSID.
  4. Current note ID, newly allocated for a spin-off.
  5. Root note ID, retained through all spin-offs.
  6. Parent note ID, the immediate source Switchnote. Sixteen ASCII zeroes encode an absent parent on an original note. Zeroes are never a valid allocated ID.

The metadata ends with creation time (u64 Unix seconds UTC), modification time (u64 Unix seconds UTC), and cover frame index (u32, zero-based). Original notes have currentId == rootId, no parent, and author FSID equal to original author FSID. A spin-off must have a new current ID different from its parent and root IDs. Editing changes the last editor and modification time; the original author and root ID do not change.

Frames

Frames appear sequentially. Each frame contains four layer records in A–D order. A layer record has a codec byte (1 for RLE), a u32 compressed byte length, a u32 CRC-32/IEEE of the compressed bytes, then the compressed data. Decoded data is exactly 230,400 row-major u8 palette indices. The decoder rejects output beyond that size, truncated packets, unknown codecs, and excess data. A compressed layer may contain at most 460,800 bytes.

RLE packets have a one-byte command. Bit 7 set means repeat the following single palette index (command & 0x7F) + 1 times. Bit 7 clear means copy that many literal palette indices from the following bytes. Both packet types allow lengths 1–128. The writer uses repeat packets for runs of at least three.

Audio

Audio track 0 is background music, starting at frame 0. Tracks 1–3 are sound effects with frame cues. Tracks are stored in order and each record contains:

Field Encoding
Track index u8, zero-based
Channels u8, mono 1 or stereo 2
BGM reference FPS u8, 1–30 for BGM (or 0 to use note speed); zero for SE. Added in payload v2.
Sample rate u32, 8000–48000 Hz
Name u16 byte length + up to 64 UTF-8 bytes
Trigger count u16
Trigger frames That many u16 zero-based frame indices, strictly increasing
PCM sample count u32, counts interleaved samples, not stereo pairs
PCM CRC u32 CRC-32/IEEE of sample bytes
PCM Signed 16-bit little-endian interleaved samples

BGM has no triggers. A sound effect may be cued multiple times. Every cue must refer to an existing frame. Total decoded PCM is limited to 32 Mi samples. To store SE without music, make track 0 an empty BGM entry with a valid rate and channel count.

With the BGM speed flag set, playback rate is current FPS / reference FPS: changing animation speed changes music tempo and pitch together. With the flag clear, BGM keeps its imported timing. SE tracks always play at their recorded speed. The v1 payload defaults to speed-linked BGM. The playback-loop flag defaults to looping for older files. When set, playback stops all Switchnote tracks at the end of the final frame; when clear, frame zero and its BGM/SE cues start together on each loop.

IDs and spin-offs

Lineage IDs are 64-bit pseudonymous identifiers, generated as the first eight HMAC-SHA-256 digest bytes rendered in uppercase hex. The HMAC key is a 32-byte per-install secret generated with the platform cryptographic random generator and kept in the encrypted console-local identity record. The message is:

ASCII("FlipnoteNX-FSID-v1") || 00 || ASCII(domain) || 00 || 16-byte nonce

This historical domain string is retained to keep existing Switchnote identities, signatures, and saved files compatible.

The portable core exposes deriveFsid(secret, nonce, domain) so the Switch frontend can provide randomness through libnx. It uses a fresh random nonce for each new note ID. Creator FSIDs are the first eight SHA-256 bytes of a domain-separated Ed25519 public key and a 16-byte one-way device tag. These values are still 16 hexadecimal characters (64 bits): an FSID is a public identifier, not a password or proof of authorship.

Gallery coin and comment writes require a random, single-use, 30-second server challenge signed with the console's Ed25519 key. The server verifies the signature, FSID/key binding, request method/path/body, and requesting IP. Coin limits and poster ownership use the full 256-bit signing-key fingerprint, independent of the shortened FSID and public device tag. Gallery HTTP is LAN-only and does not encrypt public data in transit; do not expose the development service to the public internet.

Creating a spin-off is an explicit operation. It requires the source to allow spin-offs and not be locked. It copies frames, palette, and audio; keeps the root note ID and original author credit; sets parent ID to the source current ID; allocates a new current ID; sets the new author and last editor; resets creation/modification time; and makes the child editable. The child may be saved independently without modifying the parent.

Safe writing

Signed saves serialize and validate the complete document, write a sibling temporary file, then replace the target. If the SD filesystem refuses to replace an existing file by rename, the old file is moved to a unique sibling backup, the new file is installed, and the old file is restored if installation fails. Signed loads authenticate the envelope before replacing the caller's document; legacy loads still validate all bounds, IDs, frame data, CRCs, and metadata.