Face films¶
The pilots' faces, which the radio's window plays as a pilot speaks (Radio):
.fm8 files in pilots.hog (.HOG), one for each pilot and head movement, such as
45Tigers_Plt.fm8, 45Tigers_Plt_L.fm8 and 45Tigers_Plt_D.fm8, and static.fm8 for a dead
channel. engine/game/talkie.zig decodes them.
sltool fm8 info <film> # its frames and chunks
sltool fm8 extract <film> <out-dir> # every frame as an indexed PNG file
sltool fm8 encode <frames-dir> <film> # a film of a folder's PNG files, in name order
sltool fm8 encode (fm8_encode.zig) writes one key
frame, then a delta frame for each frame after it: each block moved from the frame before where an
equal block lies within 8 pixels, else drawn in four colours where it has no more, else given whole.
It takes only exact matches, so a film of the game's comes back frame for frame, a little larger
than the game's tool made it.
Chunks¶
A film is chunks back to back, each 8 bytes of header and its data:
| Offset | Size | Field |
|---|---|---|
| 0 | 4 | fYEK, a key frame; fLED, a delta frame; fDNE, the end |
| 4 | 4 | The chunk's size, header included |
Each chunk past its header is XORed with the key A3 27 B7 DD, repeating from the header's end,
up to 8 bytes before the chunk's end; the last 8 bytes are plain in every shipped film. The game
unscrambles a chunk as it reads it (talkie_unscramble, 0x004A6E30), one chunk a tick of a
timer at 15 a second (hudmovie_init, 0x0048D030), and the end chunk, of its header alone,
ends the film (talkie_read_chunk, 0x004A6D80).
Key frames¶
A key frame (talkie_key, 0x004A6980) holds a whole frame and the palette:
| Offset | Size | Field |
|---|---|---|
| 8 | 2 | The frame's height |
| 10 | 2 | Its width |
| 12 | 2 | How many palette entries follow, 256 |
| 14 | 2 | The first entry they fill, 0 |
| 16 | 4 | Unknown: zero in every shipped film |
| 20 | 256 x 3 | The palette, red, green and blue a byte each, at first * 3 |
20 + entries x 3 |
The pixels, a byte each as an entry of the palette, row by row, compressed with RefPack |
The frames are 120 by 100, made of blocks of 4 by 4 pixels. The game looks through the entries for the colour it draws see-through, red 255, green 0 and blue 216, or red 254, green 0 and blue 215 or 216, and converts the entries for the display's colour depth.
Ten films come from earlier versions of the tool that made them: 51stWL_Plt, 51stWL_Plt_d,
BuccnrsWL_Plt, CougerWL_Plt, CougerWL_Plt_d, StingerWL_Plt_D, StingerWL_Plt_L,
STINGWL, Victorious_Brdge_Off_D and test. Their key frames hold the width before the height
and the first entry before the entries, and their delta frames their counts in other orders
(below). No pilot record names them, and the game's decoder, reading them in its own order, would
draw their pixels from the palette. Nine are faces the released films lack, the wing leaders of the
51st, the Buccaneers, the Cougars and the Stingers and the Victorious's bridge officer's death;
test holds BUCC's frames again. OpenReliant reads them by their layout: a key frame whose
entries are fewer than its first entry has the earlier order, and a delta frame the first order
whose counts lay out a chunk of its size.
Delta frames¶
A delta frame (talkie_delta, 0x004A66F0) makes the next frame from the one before, block by
block:
| Offset | Size | Field |
|---|---|---|
| 8 | 2 | The bits each entry of the map takes |
| 10 | 2 | How many blocks are given whole |
| 12 | 2 | How many motion vectors there are |
| 14 | 2 | How many blocks are drawn in four colours |
| 16 | 4 | Unknown: zero in every shipped film |
The earlier tools wrote the counts in other orders, the whole blocks' always second: the vectors,
the whole blocks, the patterns, the bits (eight of the ten films above); or the patterns, the
whole blocks, the bits, the vectors (BuccnrsWL_Plt and test).
The rest of a delta frame:
| Offset | Size | Field |
|---|---|---|
| 20 | The vectors: two 10-bit numbers each, least significant bit first, the top bit the sign, packed on to whole words (talkie_unpack_vectors, 0x004A6520) |
|
| 16 each | The blocks given whole, their pixels row by row | |
| 8 each | The patterns: four palette entries, then a word of two bits a pixel, the highest bits the first pixel's (talkie_patterns, 0x004A6590) |
|
The map: an entry for each block, row by row, of the bits above, packed on to whole words (blocks_unpack_map, 0x004BE937) |
An entry below the count of vectors moves a block from the frame before: the vector's first
number is added to a table of the rows' starts at the second, second * width + first, and the
block is copied from that far away from its own place. Any other entry, less the count of
vectors, picks a block given, the whole ones first and then the patterns (blocks_assemble,
0x004BE884). The two frames swap before each delta.
Playing¶
hudmovie_play (0x0048D120) opens a film from pilots.hog by the name after the last backslash
of its path, and decodes its first chunk. Six films of the 45th change with the squadron's name
(its pilot and Moose, talking, laughing and dying, from tables at 0x005026AC and 0x00502754):
through mission 13 the 45th Tigers' play as the 45th Volunteers', and from mission 14 the reverse,
the names compared without regard to case. A timer 15 times a second, which stands still while the
game is paused, decodes the next chunk and copies the frame into the window's image
(hudmovie_image, 0x0057C3BC), and at the end chunk loops the film or holds it for the line as
its flags say (The radio). OpenReliant reads the film whole as it
starts and unscrambles its chunks once (hudmovie.zig).
The window draws every pixel of the frame, the see-through colour among them, at the display's scale both ways, so a film keeps its shape at any window's size.