Font

From Boktai Hacking Wiki
Revision as of 15:17, 22 September 2024 by Raphi (talk | contribs)

The font stores all characters the game uses for drawing text. There are three different types of characters:

  • Narrow characters are 8x16 px in size (two tiles)
  • Wide characters in Boktai 2 and Boktai 3 are 16x16 px in size (four tiles).
  • Wide characters in Boktai 1 are 13x12 px in size (one custom tile, implemented in software by Boktai 1. Only 12x12 px are stored in the ROM, the final pixel in each row is computed depending on the pixel to the left)

The font is stored in the directory with id_low=0x6d24, id_high=0xa705 in the master file table. That directory will contain a single font file with id=0x3f51.

File format

struct FontHeader {
    /* offset 0x0 */ u16 narrowCharCount;
    /* offset 0x2 */ u16 wideCharCount;
    /* offset 0x4 */ u32 offsetToNarrowChars;
    /* offset 0x8 */ u32 offsetToWideChars;

    // Array of 8x8 px 4bpp tiles follows (32 bytes per tile)
};

Narrow characters

The font should provide one 8x16 px character for each ASCII byte between 0x20 and 0xff inclusive (= 224 characters in total). If a byte is less than 0x20, the game will not render a character for that byte:

void DrawNarrowCharacter(int x, int y, char ch, FontHeader* font) {
    // The 1st 0x20 ASCII characters are not stored in the font, and cannot be rendered
    int charIndex = ch - 0x20;
    // Each tile is 0x20 bytes in size, a narrow character is 1x2 tiles in size,
    // giving a total size of 0x40 bytes per character
    u8* tileStart = (u8*)font + font->offsetToNarrowChars + charIndex*0x40;
    // Draw two tiles, stacked vertically
    DrawTile(x, y,   tileStart);
    DrawTile(x, y+1, tileStart + 0x20);
}

Wide characters (Boktai 2/3)

The font can also provide arbitrarily many 16x16 px characters. These are referenced from strings using a two-byte sequence, stored in big-endian byte order, with the top bit of the first byte set. For example, to reference the wide character 0x123, the string should contain the two bytes 0x81 0x23. The first wide character has index 0:

void DrawWideCharacter(int x, int y, char* text, FontHeader* font) {
    char high = text[0];
    char low = text[1];
    // Combine the two bytes, and clear the top bit
    int charIndex = ((high & 0x7f) << 8) | low

    // Each tile is 0x80 bytes in size, a wide character is 2x2 tiles in size,
    // giving a total size of 0x80 bytes per character
    u8* tileStart = (u8*)font + font->offsetToWideChars + charIndex*0x80;
    // Draw four tiles
    DrawTile(x,   y,   tileStart);
    DrawTile(x+1, y,   tileStart + 0x20);
    DrawTile(x,   y+1, tileStart + 0x40);
    DrawTile(x+1, y+1, tileStart + 0x60);
}

Wide characters (Boktai 1)

Similar to Boktai 2/3, but each character is 13x12 px in size, and constructed from a single custom 12x12 px 4bpp tile (= 0x48 bytes per tile). The rightmost pixel in each row is computed from the pixel to the left of it.