Skip to content

x.1sav.icon

Owner: these specifications, under a reserved name · Applies to: a save’s bundle header · Status: normative schema, optional to carry

The picture a console puts beside a save, decoded so a consumer can display it without knowing which card the save came off. x.1sav.label answers what the save is called; this answers what it looks like. The name sits in the x tree because the value belongs to no single producer.

; ============================================================================
; x.1sav.icon - the image a console shows for a save, decoded
;
; An extension key for the Universal Saves Format, defined by these
; specifications under a name reserved in the Vendor Names registry because the
; value belongs to no single producer.
;
; The root here is the value carried under the key, not a bundle. A bundle
; validates against the format's own schema whatever this holds, since an
; extension value is `any` there; this file is what makes the shape checkable.
; ============================================================================
icon = {
0 => [+ frame], ; frames - in display order; a still icon is one frame
? 1 => png, ; banner - the wider still image, where a format keeps one
}
; A frame and how long it holds. The hold is absent on a still icon, and on a
; format whose frames all run at one rate a producer repeats the same number
; rather than inventing a second way to say it.
frame = [
png,
? uint, ; hold_ms
]
; PNG, because a consumer that needs a per-format decoder to see the picture is
; the consumer this key exists to spare. The ceiling is headroom: the largest of
; these on real hardware is a GameCube banner at 96x32, and eight icon frames at
; 32x32 beside it.
png = bstr .size (1..65536)

frames is the icon in display order, and a still icon is one frame. hold_ms is how long a frame shows before the next, which a GameCube save varies per frame and other formats do not. banner is the wider still image a format keeps beside the icon rather than instead of it: a GameCube save’s 96x32 banner, a VMU’s 72x56 eyecatch.

A producer decodes to PNG. A PS1 icon is 4bpp against a 16-colour CLUT, a GameCube icon is RGB5A3 or CI8 depending on two bits in the directory entry, and a VMU icon is 4bpp against a palette in the VMS header. A consumer should need none of those decoders.

A producer MUST NOT improve the picture. Scaling it up, correcting its palette, compositing a background behind transparency and reordering frames all produce something the card does not hold. The native bytes stay in the payload, where a writer reads them through the format’s own key.

The header of the bundle that is the save, because the image bytes are in the save’s payload and travel with it: PS1 in the first block, VMU in the VMS header, GameCube at the image address the directory entry points to.

An aux part is not the place. Beside the save it is linked only by inference from slot and path; inside the save’s own bundle it joins that bundle’s content hash, which is the save’s identity.

  • Native pixel data. Decode it, or omit the key.
  • A PS2 save’s icon. icon.sys and the .ico files it names are already parts inside the save’s bundle, each with its own path, slot and dirent, and a .ico is a 3D model rather than a picture.
  • Screenshots and box art. An aux part with a content_type.
  • The icon’s native format, palette or animation flags. The format’s own key, such as x.1sav.dirent.gc-mc.

Only when the producer decoded the picture. A producer that cannot decode a format’s icon omits the key. N64 controller pak notes, Neo Geo saves and Saturn backups have no picture at all.