Westwood Unicode BitFont Format
In Red Alert 2, Westwood Studios added support for the basic unicode range up to 65,536 symbols. This font type is rather different from the earlier WWFont, going back to 1 bit per pixel, and using a completely different header which seems to be based on that of the earlier non-unicode BitFont format.
The game has only one single font file in this format, namely, game.fnt. While the font's symbols technically represent the unicode range, its 0 to 255 range actually corresponds to Windows-1252 encoding, whereas in normal unicode, this range should be ISO-8859-1. This means the range 128 (0x80) to 160 (0xA0) are used as actual symbols, while in ISO-8859-1 this range only contains control codes. However, the Euro symbol (€) in game.fnt is only at its original position at index 8364 (0x20AC), and not at the 128 (0x80) position where it would be expected in the Windows-1252 encoding.
The font format starts with the following header.
|0x00||Char||Format||Literal string "fonT".|
|0x04||UINT32LE||IdeographWidth||Override for the standard ideographic width (symbol 0x3000). The logic that uses this is rather convoluted, though, and prefers taking the double of the width of the space character (0x32) instead.|
|0x08||UINT32LE||Stride||Stride of all font image data.|
|0x0C||UINT32LE||FontHeight||Overall font symbols maximum height, in pixels.|
|0x10||UINT32LE||FontWidth||Overall font symbols maximum width, in pixels.|
|0x14||UINT32LE||Count||Amount of saved symbol images in the file. Images can be used for any unicode symbol, and can even be used multiple times.|
|0x18||UINT32LE||SymbolDataSize||Size of one symbol data block. Should always match |
|0x1C||UINT16LE[0x10000]||UnicodeTable||Array representing the full 65,536 symbol unicode range, referencing which stored symbol block to use for each symbol. Values in this table should be subtracted by 1 to get the index, with an index of -1 (after subtraction) meaning 'empty'.|
Since there is only one font of this type in the game, namely game.fnt, it is difficult to see if there is an actual meaning to the unknown value at offset 04, which doesn't seem to have any clear link to the rest of the data.
Though the width and height are saved as 32-bit integers in the header, they are limited by the fact symbol widths in the image data are saved in a single byte, and by the fact the SymbolDataSize, being
1 + (Stride * FontHeight), must also fit in a 32-bit integer.
The actual font data is very simple straightforward 1 bit per pixel, using the stride and height indicated in the header. The data is prefixed by a byte that gives the symbol width.
Since all symbol image blocks are the same size, the block of data for a specific unicode code point CodePoint can be found by getting the image index from
index = UnicodeTable[CodePoint] - 1. If index is -1, the symbol is empty. Otherwise, the data block for that symbol can be found at the address
2001C + SymbolDataSize*index.
|0x00||UINT8||SymbolWidth||Width of this symbol. This does not change the stride of the following data, but only limits the final image width to show.|
|0x1||BYTE[Stride*FontHeight]||SymbolData||1-bpp image block with the stride and height given in the header.|
The font does not contain any padding between symbols. Padding is applied automatically by the game, and seems to be one pixel between the symbols.
Technically, data could be hidden in the SymbolData, in the portion of the full stride that is cut off by the local width value.
The following tools are able to work with files in this format.
|Name||Platform||View images in this format?||Convert/export to another file/format?||Import from another file/format?||Access hidden data?||Edit metadata?||Notes|
|OS Font Editor||Windows||Yes||Yes||Yes||No||N/A||Can import TrueType fonts. Has issues saving more than 32767 distinct symbols into the font due to handling the data as signed 16 bit. Can't change the font dimensions.|
|Westwood Font Editor||Windows||Yes||Yes||Yes||No||N/A||Re-optimises the entire font on save, resulting in slightly longer (±5 second) save times.|