Skip to content

Game Font

Horace And The Spider edited this page Jul 21, 2026 · 2 revisions

GameFont

The Bloodwych 439 GameFont file is a headerless 640-byte bitmap table:

  • 128 glyphs, addressed by character_code & 0x7f.
  • Five bytes per glyph (character_code * 5).
  • Each byte is one horizontal scanline, top to bottom.
  • All eight bits form an eight-pixel scanline, bit 7 through bit 0 from left to right. Most printable designs occupy only the lower six or seven bits.
  • The resulting storage/rendering cell is 8 x 5 pixels.

The 68k routines at GameFont calculate code * 5, loop five times, and advance the screen destination by one scanline after each byte. The data also validates the interpretation directly: character A is represented by rows 0c 12 3f 21 33.

tools/gamefont_converter.py generates an indexed, transparent PNG sheet, JSON metadata, and a byte-identical reconstructed font. The JSON retains all eight bits of every original row. That makes the conversion safe for future editing and binary re-export.

A spritesheet is preferable to TTF/WOFF as the canonical representation. A normal outline webfont adds hinting, baseline, spacing, and resampling concerns that do not exist in the game. The browser should render the indexed sheet with nearest-neighbour scaling. A generated OpenType bitmap wrapper could later be offered for convenience, but it should remain a derivative asset rather than the editable source.

Atari ST four-plane graphics

The AMOS graphics loader correctly decodes the native repeating unit:

offset + 0: plane 0, big-endian 16-bit word
offset + 2: plane 1, big-endian 16-bit word
offset + 4: plane 2, big-endian 16-bit word
offset + 6: plane 3, big-endian 16-bit word

Each eight-byte unit produces 16 indexed pixels. For pixel x, with bit 15 - x from each plane, the palette index is:

p0 | (p1 << 1) | (p2 << 2) | (p3 << 3)

The AMOS write routine performs the exact inverse operation, so the codec can support edited graphics and byte-for-byte round trips. This codec should be a small shared core service, usable in both Python and TypeScript test vectors.

The hard-coded AMOS Data sections do not describe the pixel encoding. They describe how sequential 16-pixel strips are arranged into many differently sized pictures. The current pairs appear to provide strip widths and heights, with sentinel values such as 9999 allowing a remaining run to continue. This geometry layer should become versioned, named JSON descriptors rather than be embedded in the decoder. A descriptor should record:

{
  "id": "head_parts_00",
  "segment": "Head_Parts",
  "byte_offset": 0,
  "width_pixels": 16,
  "height_pixels": 111,
  "palette": "characters",
  "transparent_index": 15,
  "role": "character-template"
}

Offsets should be derived cumulatively and then checked against the segment size. Width must be a multiple of 16 for the raw planar layer; any visual crop, hotspot, or composition bounds belong in separate metadata. This removes the loader's current uncertainty between storage width and displayed/cropped width.

Clone this wiki locally