Bitmap fonts
LIB/CHAR006.CHR, CHAR009.CHR, CHAR011.CHR, CHAR013.CHR
Four variable-width glyph banks that draw all of the game's text.
The CHAR###.CHR files are the game's bitmap fonts. Each holds up to 256 variable-width glyphs indexed by character code, stored as 8-bit palette indexes; the number in the file name is the font's line height.
A font file has two parts. The first is a 516-byte header (FontHeader): a table of 256 two-byte slots, one per character code, followed by two metrics words. The second is the glyph records (Glyph), stored one after another. A slot in the table holds the file offset where that character's glyph record starts, so the header is how you find the glyphs. Zero means the character has no glyph.
File Layout
Header
3 fields · 516 bytes
65 is A, slot 97 is a.
Zero means the slot has no glyph, which includes space (32). Offsets are absolute from file start.
datarender
Some tools call this word default_width; that name does not describe its use.
databehaviour
Glyph heights equal this in all but one glyph (\ in CHAR013). Some tools call it default_height. Equals the numeric suffix in the file name for every shipped font.
datarenderbehaviour
Glyph
8 fields · 18 bytes
The draw path does not use this prefix; its general purpose is unknown. CHR does not carry the INFOILBM magic used by archive images.
databehaviour
U blitter (the routine that copies a glyph's pixels to the screen) skips every sample equal to this byte before applying the caller's colour offset.
It is 0x60 in all shipped glyphs.
databehaviour
0x55 (U) in all shipped glyphs; selects the byte-per-pixel planar blitter.
databehaviour
Zero in all shipped glyphs.
databehaviour
Zero in all shipped glyphs.
databehaviour
data
Equal to line_height (default_height) for every glyph except one: \ in CHAR013 is 11 high in a font whose line height is 13.
data
p holding the columns where x % 4 == p, each scanned top to bottom.
Sample 0x60 is transparent; every other sample is an opaque pixel whose palette entry is the sample plus the text's colour offset, not a level of opacity. The fonts use 0xf0 for solid ink and 0xee, 0xef and (once) 0xdf for dimmer edge shades. See "Pixel Order".
datarender
The fonts also contain the German umlauts, at their DOS code page 437 positions: ü 0x81, ä 0x84, Ä 0x8e, ö 0x94, Ö 0x99, Ü 0x9a.
Example: the letter A in CHAR011.CHR
A is character code 65, so its slot in the header is at byte 65 x 2 = 0x82.
0x0082 10 0c glyph_offsets[65] = 0x0c10, so the glyph for "A" starts at 0x0c10
0x0200 08 00 baseline = 8
0x0202 0b 00 line_height = 11
0x0c10 00 00 00 00 00 00 00 00 reserved_prefix
0x0c18 60 transparent_index
0x0c19 55 record_type ("U")
0x0c1a 00 00 x_offset = 0
0x0c1c 00 00 y_offset = 0
0x0c1e 09 00 width = 9
0x0c20 0b 00 height = 11
0x0c22 pixels, 9 x 11 = 99 bytes, in four planes:
plane 0 (columns 0, 4, 8): 60 f0 60 60 f0 60 60 f0 60 60 60 60 60 60 60 60 f0 60 ee f0 ee f0 60 f0 f0 60 f0 60 60 60 60 60 60
plane 1 (columns 1, 5): 60 ee 60 f0 60 f0 60 f0 60 f0 f0 f0 f0 f0 f0 60 f0 60 60 60 60 60
plane 2 (columns 2, 6): 60 60 60 60 ee ee f0 f0 f0 f0 f0 f0 f0 f0 ee ee 60 60 60 60 60 60
plane 3 (columns 3, 7): ee 60 f0 60 f0 60 f0 60 f0 60 f0 f0 f0 f0 60 f0 60 f0 60 60 60 60
Planes are counted per glyph, not across the file. Column x of a glyph is always in plane x % 4, counting from the glyph's own left edge, so every glyph starts again at plane 0 and its pixel bytes are self-contained. A glyph 9 columns wide has three columns in plane 0 and two in each of the others. Each plane is read top to bottom, and the planes are then interleaved on screen. The drawing also shows where the header's baseline and line_height fall: a capital sits on row 8 (the baseline), and a text line is 11 rows tall.
In text form the same glyph is below: . is the transparent value 60, # is full ink f0 and o is the mid shade ee, which softens the corners of the strokes.
...o#o...
...###...
..o###o..
..##.##..
..##.##..
.#######.
o#######o
##o...o##
##.....##
.........
.........