.SPR sprites

The game's 2D imagery, drawn by WinVFX: the sprite sets in resource.hog.

These are the interface, not the world: the HUD, menus, cursors, briefing and loadout screens, the news reader, kill tallies, and a per-ship schematic. They are not model textures, which is covered under What sprites are not.

sltool spr info <sprite>                # what the set contains
sltool spr ls <sprite>                  # every block, with kind and size
sltool spr extract <sprite> <out-dir>   # every shape as an indexed PNG, with the name a mod uses
make sprites                            # every shape into game/sprites

Layout

Offset Size Field
0 4 1.40, stored as four raw bytes rather than a number
4 4 Block count
8 8 x count Directory: a u32 offset and a u32 that is zero in every entry of every file

Entries are in ascending offset order, so a block runs from its own offset to the next one, or to the end of the file.

Blocks

Nothing records what a block is, and three kinds occur. They have to be told apart by structure:

Kind Recognised by
Shape Parses as a shape header whose rows fit inside the block
Palette 768 bytes whose every byte is a 6-bit level, so never above 0x3F
Remap table 256 bytes, the remainder
Placeholder Neither, and carries no rows

Size alone will not separate them: some shipped shapes are exactly 768 or 256 bytes long. The palette test is what resolves it, since every such shape contains 0xFF.

The placeholders hold bounds near maxInt(i32) and no pixel data. They occur in LAUNCH.SPR, ifhard.spr and ifsoft.spr.

A remap table maps each palette index to another. The HUD sets open with a run of them, the first being the identity, 00 01 02 ... FF.

Shape

Offset Type Field
0x00 u16 x2 What VFX_shape_bounds returns: a height in the low half and a width in the high. For the pause menu's shapes, y2 + 1 and x2 + 1; constant across a file in the ship schematics
0x04 u16 x2 What VFX_shape_origin returns, down in the low half and across in the high, which the pause menu adds to where it draws a shape: (1, 1) for each of its shapes
0x08 i32 x4 x1, y1, x2, y2, inclusive
0x18 The rows

Bounds are signed and can be negative, which is how a sprite is centred on its anchor rather than its corner. Width is x2 - x1 + 1.

Rows

One run-length encoded row per scanline, top to bottom. Each opcode starts with a control byte whose low bit picks the kind and whose upper seven bits are a count:

Control byte Meaning
0x00 End of row. The rest of the row stays transparent.
Even, count > 0 Repeat the next byte count times
Odd, count > 0 Copy the next count bytes
0x01 Skip the next byte's worth of pixels, leaving them transparent

Pixels are palette indices. What a row skips, or leaves past its end, stays transparent. The game's VFX_shape_draw (winvfx16.dll, 0x10003596) draws every pixel of a run in its palette colour, index 0 included. OpenReliant draws index 0 so only for the crew in the rooms, who show over a movie's frame (The crew), and leaves it transparent everywhere else (#518).

Palettes

A palette is 256 RGB triples at 6 bits per channel, the VGA convention. Expanding to 8 bits by repeating the top bits into the bottom keeps full scale full: (v << 2) | (v >> 4).

A file may carry several, and a shape uses the nearest one at or before it, so a set can hold groups that each have their own. CAPSHIPS.SPR is a run of such groups, each a palette and two ships, so a pair of ships shares a national colour scheme.

Most sets carry no palette at all, including every ship schematic. Those shapes are drawn with whatever palette the game has loaded, and sltool spr extract falls back to greyscale for them.

Unknown: which palette that is. It is not the first 768 bytes of palette.ccb: those are 6-bit values, but they colour the schematics as noise while leaving the silhouettes clean.

What sprites are not

None of the textures the .SHP models name is a sprite set:

  • No model texture name matches any .spr, under any prefix.
  • Most sets are named <ship>SCEM.SPR: small, one per ship, the schematic shown in the interface.
  • The rest are named for their screens: BRIEF, FRONTEND, HUDHARD, LOADOUT, CURSORS, NEWSREP, KILLS, LAUNCH, CAPSHIPS.

The models' textures are in the texture caches.

Prior art

The RLE encoding and the shape header were decoded by DMJC's StarLanceDecomp from VFX_shape_blit_unclipped in WINVFX8.DLL, which also identified the palette blocks and the nearest-preceding-palette rule. Everything above was re-derived against the 269 shipped files; the remap tables, the placeholders, the palette content test that separates blocks whose sizes collide, and the texture findings are additions.

Edit this page on GitHub. The documentation is under CC BY-SA 4.0.