File Formats

What NextDither reads, and what it writes. The two picture formats below are NextDAAD's own on-hardware formats. That is true of the size and shape rules below: a file that breaks them is one the game's loader refuses outright, not one that displays wrong. It is not true of the reserved transparency colour, covered later on this page and in Transparency - a file that breaks that rule loads and displays fine, just with the wrong colour where a hole should have been. Every size and byte layout here is checked against this project's own pack module (crates/nextdither-core/src/pack/) and against NextDAAD's format reference, manual/reference/picture-format.md in the NextDAAD repository.

.NX2 and .NXI

Extension Width Max rows Screen coverage
.NX2 320 px 256 Full screen, border included
.NXI 256 px 192 Classic paper area, inset by the border

Width is implied by the extension and is never stored in the file. NextDither enforces both ceilings itself, before packing a single byte, through pack::validate_layer2_shape - a 320-wide frame past 256 rows, or a 256-wide frame past 192, is refused with a named error rather than written as a file the loader would reject. Adjusting an image covers the Frame group where width and height are set.

Both formats share one byte layout: a 512-byte palette (256 entries, 2 bytes each) followed by one index byte per pixel, row-major. Total file size is therefore exactly 512 + width * height bytes. At each format's usual full size that comes to:

  • .NX2 at 320x256: 512 + 320*256 = 82432 bytes.
  • .NXI at 256x192: 512 + 256*192 = 49664 bytes.

A frame smaller than the ceiling is legal and simply produces a smaller file - the two figures above are the sizes at each format's maximum height, which is also what the stock "NextDAAD Layer 2" preset targets for .NX2. Row-major holds for .NX2 as well as .NXI, even though 320-wide Layer 2 video memory is column-major on the actual hardware - that is a property of the display surface, not of this file, and NextDither writes rows.

RGB333 packing

Each palette entry is a 9-bit colour (3 bits per channel) packed into 2 bytes, exactly as colour::Rgb333::hi_byte/lo_byte and pack::pack_nxp write it:

  • Byte 0: RRRGGGBB - the top two bits of blue.
  • Byte 1: bit 0 is blue's remaining (least significant) bit; bit 7 is the Layer 2 priority flag; NextDither always writes bits 1-7 zero, so it never sets the priority bit.

NextDither reduces an 8-bit-per-channel source colour to 3 bits by nearest reconstruction - the 3-bit value whose expansion back to 8 bits lands closest to the source - rather than by truncating the high bits. This is more accurate than a simple mask-and-shift, and it is also what narrows which source colours land on the reserved value covered next.

Slot 255 and the reserved colour

Index 255 is the one slot NextDAAD's interpreter stamps with the transparent colour on every picture load, and no palette entry anywhere may pack to first byte $E3 except at index 255 itself. NextDither carries both rules through its slot policy and its forbidden-colour set (the stock preset forbids 0xE3, matching NextDAAD's reserved value exactly). This is covered in full, including what happens when it goes wrong, in Transparency and in Troubleshooting.

Compressed pictures: .NX2.ZX0, .NXI.ZX0, and the 8.3 synonyms

NextDither can ZX0-compress a bitmap on export (the Bitmap, ZX0 compressed format), in one of two ways governed by the preset's zx0_mode (see The preset file). The default, single-stream, compresses the whole file as one stream, so decompressing it yields exactly the same 512-byte-palette-plus-pixels layout described above. two-stream instead compresses the palette and the pixel data as two separate ZX0 streams, matching gfx2next -zx0 -pal-embed - each half has to be decompressed on its own, and concatenating the two results gives the same layout.

It must be classic ZX0 (v1), never v2. The two are mutually unreadable, and a ZX0 stream carries no version marker of its own, so a v2 file loaded as if it were v1 either fails silently or decompresses to garbage - NextDAAD's own format reference does not commit to which, and nothing in this project resolves that either, since NextDither never decodes a foreign v2 stream. Either way, it is not a file you can rely on being refused with a clear error: use classic v1. NextDither's own compressor only ever emits classic format (pack::zx0::zx0_compress's own comment records this was checked directly against the reference z88dk-zx0 tool's actual output, not assumed from its help text), so anything NextDither itself writes is safe. The risk is only if a .NX2.ZX0/.NXI.ZX0 file is produced or re-compressed by some other tool - it must also emit classic v1.

Naming follows NextDAAD's own extension convention: append .ZX0 to the bitmap's whole name (cellar.NX2.ZX0), or, with short ZX0 extensions ticked in the Out tab, the 8.3 synonyms .N2Z (for a 320-wide .NX2) and .NXZ (for a 256-wide .NXI) - for plain-FAT setups without long filenames. Either form decompresses to the identical file (write::zx0_path).

.NXP palette files

NXP palette exports the 512-byte palette alone, with no pixel data - pack::pack_nxp, the same function that writes the palette half of a bitmap export. Useful for capturing or comparing a palette independently of any one picture.

.png

PNG exports an ordinary indexed PNG of the converted result - the same pixels and palette as the bitmap export, in a format any other image viewer or editor can open, for checking your work outside NextDither. See Exporting for how export formats are chosen and combined.