Skip to content

File format

Sprites and animation

How a frame is positioned, how a direction picks a stored facing, and why the file contains no timing at all.

.imp

In short

Sprite placement was the community's longest-running unsolved problem, and it is solved. Animation control — what plays next, and which stored artwork a facing resolves to — is read out of the game's own decoder. Playback speed is not in the file, and that negative result took more work to establish than either of the positives.

What was found

The placement rule

Measured

A frame's top-left corner is the anchor point, plus the stored placement pair, minus half the frame's width and height. The placement is measured to the centre of the frame, not its corner, and it is added rather than subtracted.

Two things follow. Shipped vertical values are negative, because artwork grows upward from where a figure stands. And re-cropping art changes the placement it needs by half the crop on each axis — which is exactly why the community's crop-and-re-centre workaround always nearly worked, with an error that moved when you edited rather than staying put.

Why the old editor broke sprites

Measured

The tool the community used for years writes no placement data at all. Placement is authored per frame and is not recoverable from the artwork: across 28,447 unit frames the horizontal value is independent of frame width, and height explains only about half of the vertical value.

So art that passes through that tool cannot have its placement reconstructed by any amount of cleverness. It has to be written back deliberately.

Five stored facings, eight directions

Measured

A sprite normally stores about five facings and covers eight directions by mirroring. A direction at or past the stored count folds back onto a stored one and raises a flag, and that flag makes the drawing code negate the horizontal placement — a horizontal flip, using the same value the placement rule above turns on.

Which compass bearing is direction zero is still unknown. Two separate rotations sit between a script-level facing and a stored index, and neither is anchored to a real bearing by anything measured here.

There is no frame timing in the file

Measured

No field of a sprite file is read by the game as a duration, delay, frame rate or tick count. Playback cadence comes from a global counter, not from the artwork.

The first version of this argument was wrong, and the document says so: it claimed a record could only be reached by one kind of arithmetic, which code the same page already cited disproves. The negative only stands because it was re-derived a second way after that.

Some unread bytes are not garbage

Open question

Several fields the game never reads are nevertheless too consistent to be leftover memory: one holds the same value in 4,661 of 4,667 records, another in all 4,667. And 501 records carry a patterned value in bits the game masks away before looking.

Something wrote those deliberately — most likely the original authoring tool. What they meant is open.

Palette entries are stored blue, red, green

We were wrong about this

This project's decoder had the channel order reversed, which swapped red and green while leaving blue correct — a bug that looks like nothing at all on any material where red and green are close. Writing known pure colours into a palette and photographing what the game drew settled it, and also refuted the community specification's description of the same field, which this project had accepted on trust.

Still unknown

Named as open rather than guessed at.

  • The animation tick period in milliseconds.
  • Which compass bearing direction zero points at.
  • What the deliberately-written but never-read bytes were for.
  • A handful of sprite-type values that appear in slots the game does search, and that nothing explains.

All file formats