Font

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

The font stores all characters the game uses for drawing text. There are two types of characters: Narrow characters are 8x16 px in size (two tiles), and wide characters are 16x16 px in size (four tiles).

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.

Warning: In Boktai 1, wide characters are structured differently than described here. In Boktai 1, each wide character is 0x48 bytes in size, whereas in Boktai 2 and 3 each wide character is 0x80 bytes in size. The format of Boktai 1 wide characters still needs to be reverse engineered.

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

The font can also provide arbitrarily many wide 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);
}