Fonts and lettering in TFX
Open the font browser for all fourteen sets, editable samples, complete byte tables and TTF / WOFF2 conversions. Each set also has a PNG character sheet and a CSV table. This catalogue covers the September 1994 CD executable, version 2.05; it does not imply that every earlier build has identical lettering.
We already had partial captures under presentations and cockpits. This brings them together with the other writers, decodes their storage directly and checks the results by executing the original drawing routines.
The browser's magnification uses physical screen pixels: at 1×, each source pixel occupies one display pixel. Both sample previews and the character grid compensate for the browser's reported pixel ratio and align to the screen grid, including after a zoom change or moving between monitors.
What is there?
| Set | Native storage | Layout and use |
|---|---|---|
| Presentation 0 | Proportional monochrome records | 13-pixel line box; presentation scripts |
| Presentation 1 | 64 columns of seven row masks | Eight-pixel cells, nine-pixel line box |
| Presentation 2 | 64 columns of six row masks | Six-pixel cells and line box |
| Presentation 3 | Proportional monochrome records | 16-pixel line box; drawing uppercases ASCII letters |
| Presentation 4 | Proportional monochrome records | 12-pixel line box; drawing uppercases ASCII letters |
| Presentation 5 | Proportional monochrome records | Seven-pixel line box; drawing uppercases ASCII letters |
| Presentation 6 | Proportional records plus an extended bank | Seven-pixel line box; translated briefing text |
| Presentation 7 | Proportional monochrome records | 18-pixel line box |
| Presentation 8 | Proportional monochrome records | Nine-pixel line box |
| Presentation 9 | Proportional monochrome records | Seven-pixel line box |
| Driver / diagnostics | 64 columns of six row masks | Eight mask columns, nine-pixel advance; diagnostic text and recording marker |
| Widgets / planner | 115 columns of eight row masks | Mixed case, accents and interface symbols, selected by a 256-byte lookup |
| HUD / instruments | 51 monochrome records | 8×6 masks with proportional advances; HUD, MFD and arming captions |
| Cockpit sprite lettering | 45 four-plane indexed sprites | 8×5 images, four-pixel advance; clock, radio and instrument readouts |
The presentation numbers are the original FONT style IDs. The descriptive
names of the other exports are ours. Glyph dimensions and advances can differ
from the line box: neither the record width nor the visible ink width alone is
sufficient to reproduce native text layout.
Encodings and layout quirks
The browser lists all 256 byte inputs for each writer, including control bytes, aliases, blank cells, skipped entries and unavailable glyphs. CP437 labels identify those bytes; they are not a promise that the displayed image has that meaning. For example, the HUD question-mark byte draws an A.
Most proportional fonts use a custom 64-entry lookup addressed by byte & 63.
A signed lookup value of −1 skips drawing. Some mapped pointers are absent or
outside usable font data, notably in presentation style 0. Those cells are
marked unavailable rather than replaced with invented lettering. A physical
slot view exposes the decoded bank entries separately from their byte aliases.
For proportional fonts this is the set of usable slots reachable through the
writer, including safe addresses shared with adjacent tables.
The fixed presentation and driver writers also mask to six bits, but any byte
whose low six bits are zero terminates the string. Thus @, byte 0x80 and
byte 0xC0 can stop drawing, just as NUL does. Their raw slot zero is still
visible in the fixed presentation and driver banks.
Presentation styles 3–5 uppercase ASCII a–z when drawing; their width helpers measure the original byte. Style 1 gives a capital I a four-pixel advance after the first character, although measurement counts eight. Styles 0, 7, 8 and 9 subtract a final width trim of 2, 2, 1 and 1 pixels respectively. Style 6's extended width helper excludes the extra drawing gap. The tables retain both drawing and measured advances where there is a presentation width helper.
Style 6 has a separate pointer and width table for bytes 0x80–0xFF. Its extended glyphs begin two pixels above the normal origin. There are gaps and unsafe entries: byte 0xFE reads into a relocated pointer instead of a stable width entry, and a bounded native drawing probe faults in the monochrome blitter. Its raw file width byte is zero, but becomes 10 in the harness after relocation. We mark the metric unavailable instead of presenting that address byte as a font width.
The widget writer starts two pixels above the supplied Y and clamps that start to the top of the screen. Its advance is the per-byte table value plus one. The HUD handles a literal space separately with a three-pixel advance.
The cockpit sprite writer treats CR and LF as skips, not line breaks. Its shared
lookup can select slots 45–50 even though pitfont.spr contains only 45 frames.
Those inputs are unavailable. Indices 1 and 2 form the lettering, index 107 is a
background write, and index 0 is transparent. The browser's source-index colours
are diagnostic colours, not a selected in-game environment palette.
Native formats and addresses
Offsets below are relative to the reference executable's code object at file
0x70aa4 or data object at file 0xedaa4. Pointer values are relative to the
data object. The browser's individual source offsets are absolute file offsets.
The decoder rejects an executable with a different SHA-256:
2cc41a39fa95a2c9fd883c4220d7bdd9cca00c543c89b019eb270763f59d1d9a
A monochrome record begins with byte-sized width and height. Each following row
contains ceil(width / 8) bytes, most significant bit first. Pointer and width
tables are separate. Proportional presentation styles share the signed map at
data+0xcc2f.
| Style | Pointer table | Width table | Drawing gap | Space |
|---|---|---|---|---|
| 0 | data+0xd23c | data+0xcd5b | 2 | 6 |
| 3 | data+0xcdc0 | data+0xcc6f | 1 | 4 |
| 4 | data+0xce8c | data+0xcca2 | 2 | 6 |
| 5 | data+0xd024 | data+0xccd5 | 1 | 5 |
| 6 | data+0xcf58 | data+0xcd8b | 1 | 3 |
| 6 extended | data+0xd2fc | data+0xd484 | 1 | — |
| 7 | data+0xd08c | data+0xccef | 2 | 17 |
| 8 | data+0xd11c | data+0xcd13 | 1 | 5 |
| 9 | data+0xd1ac | data+0xcd37 | 1 | 5 |
Fixed-font row tables start at code+0x5bb51 (style 1), code+0x63f02
(style 2) and code+0x509e5 (driver). These store one byte per character per
row, least significant bit first, with a row stride of 64 bytes.
The widget map is at code+0x6103d, advances at code+0x60f49, and masks at
code+0x61132 with a row stride of 115. The HUD map, widths and pointers are at
data+0x7a44, data+0x7a84 and data+0x7ab8 respectively.
spr/pitfont.spr starts with a frame count and little-endian 16-bit offsets.
Each frame stores half-width, height, then four VGA planes. For these 8×5 frames,
the byte at (x % 4) * 10 + y * 2 + x / 4 selects a pixel. The text lookup is
at data+0x7688.
Modern font exports
There are 15 TTF / WOFF2 pairs: one for each set and an additional Cockpit Coverage face. These contain square pixel outlines with recovered bearings and advances. They do not smooth or redesign the original artwork.
One source pixel is 64 font units in a 1,024-unit em. At a font size of 16px, one source pixel occupies one screen pixel. Integer multiples give crisp magnification. A small TrueType instruction program suppresses automatic hinting that would otherwise move some of the deliberately uneven pixel edges.
The character maps provide three ways to address glyphs:
- Printable bytes have conventional CP437 Unicode aliases for convenient typing.
U+E000 + byteselects the exact drawable native byte mapping.U+E100 + slotexposes a physical glyph slot. Style 6's extended slots start at slot 0x100, so their addresses start at U+E200.
Terminators, skipped inputs and unavailable pointers have no exported glyph.
A .notdef marker makes unsupported text visible. The fonts cannot reproduce
string termination, clipping, palette changes or context-dependent I spacing;
these remain responsibilities of a native-style text writer. Browser font
fallback can also make unsupported typed text look different from the native
preview.
The regular Cockpit face contains indices 1 and 2 as monochrome ink. The Coverage companion includes every nonzero source pixel, including background writes. Neither is a colour-font reproduction of the original sprites; their palette indices remain available in the decoded tables.
These are conversions of the game's lettering. They do not grant a new licence to the original artwork.
Reproduction and checks
Run python3 tools/analyse_fonts.py with Pillow, fontTools and Brotli installed.
It reads the reference executable and extracted sprite bank without modifying
an installation. It writes the catalogue, sheets, tables and portable fonts
under analysis/fonts/.
The export check reloads every TTF through FreeType and compares 3,998 glyph rasters and advances with the decoded bitmaps, including physical slots and both cockpit layers. It checks WOFF2 character maps and horizontal metrics too. Files have fixed timestamps for reproducible output.
tools/verify_font_analysis.py uses the existing Unicorn VGA harness in the
adjacent tfx-cpp/tools checkout (--cpp-tools can select another location).
It executes the original presentation, widget and HUD writers and compares
whole 320×200 frames, including the mask of written pixels: 3,023 character
frames, with 49 unsafe entries deliberately excluded. All 45 cockpit frames
are independently checked through the native sprite blitter. Proportional width
helpers have 2,047 byte checks across their eight styles; the relocated, unsafe
style-6 byte 0xFE is excluded.
The driver row decoder is covered by the export raster checks; the driver text writer is not part of this native execution run. Full results are in native-checks.json. The decoder is analyse_fonts.py; the separate native check is verify_font_analysis.py.