Font file

From Stunts Wiki

Stunts (BB 1.1) includes three font files, which have a ".FNT" extension and a very unique format. The files in question are FONTDEF.FNT, FONTN.FNT and FONTLED.FNT. FONTDEF (probably standing for "Default Font") is the thick font used for the text in menus, dialog boxes and buttons. FONTN is used for longer text, for example, car descriptions in the showroom, scoreboard rows and the high score information when viewing the complete track in 3D in the Track menu. FONTLED only contains digits and a couple of punctuation characters and is used for the lap times in the replay menu panel.

While this font format appears to be unique to Stunts, it is pretty flexible and worth considering for other projects. The three fonts included with Stunts are bitmaps of character height 8, but vary in width. As a matter of fact, FONTDEF and FONTN are variable width, that is, each character can have an individual width. Yet, Stunts allows for fonts of other characteristics to be loaded. If the files are modified, Stunts adapts to the changes very well.

Font format features

Stunts font format is strictly bitmap based, but very configurable. It allows for up to 256 characters that can be mapped to 256 code points from 0 to 255. Unused code points don't need to have their glyphs defined, which saves space. Fonts can have either fixed or variable width. For variable width, widths up to 255 are theoretically possible, although Stunts surely must have a much smaller limit. With fixed width, this could go up to 32767 or perhaps 65535 depending on how the values are interpreted. Character height can also, in theory, go up to these values. As of 18 March 2021, it is not known whether height can be made variable too.

Format structure

After an analysis made by Cas on 17 March 2021, the font format was found to have the following structure:

Header

  • (1 byte) Fixed = 3
  • (11 bytes) Fixed: all zeroes
  • (1 word) Not fully understood: 1 or 2 for variable width, but has to be 1 for fixed width
  • (1 word) Font height
  • (1 word) Font width (if width is variable, this is zero)
  • (1 word) Unknown: Always 8 in the three original fonts and doesn't seem to do anything if changed
  • (1 word) Variable width flag: 1 for variable width, 0 for fixed width

Total: 22 bytes

Index

Following the 22-byte header, comes the index. It's exactly 256 words long (512 bytes) and each word points to the position in the file where the glyph is defined for each code point from 0 to 255 in order. If a glyph is not defined for a certain code point, its index is set to zero. Valid indeces count from 0 at the beginning of the file. This means that position 534 (starting from 0) is the first byte in the file available for glyph definition and is used as such in the three fonts originally provided with Stunts. This could, however, be skipped freely and additional information stored there.

Glyphs

For each code point with a non-zero index, a glyph has to be defined. If the font is variable width, the first byte in the glyph region contains the character width in pixels. For fixed width fonts, this byte is omitted. Following are the glyph rows. The number of bytes to represent each row depend on the character width. One byte is required for every 8 pixels. In other words, the number of bytes per row will be the ceiling of the division of the character width by 8. Stunts fonts go over 8 pixels wide, but never reach 17. However, it has been tested it actually supports at least up to 32 pixels character width and 16 pixels character height.

The format itself could theoretically define sizes up to 255x255, though it is unlikely that Stunts can support characters this large. There is another limit which has to do with indeces being 16bit words. Depending on whether Stunts treats them as signed or unsigned, characters beyond the 32kbyte or 64kbyte limit could not be referenced. A 32x32 font takes up enough space so that it would overflow the first limit if all characters are defined. Yet, since this format allows for only defining some characters, larger but limited fonts can still be created. The first limit for fixed width fonts and all characters defined would allow a square font up to 31x31. The second limit, under the same conditions, would allow up to 42x42.

Pixels in each row are defined in ascending order (from lower bit to higher bit) from right to left and can be 0 for off or 1 for on. If more than one byte is used, the format is little-endian. Unused bits are left as 0 in original Stunts fonts, which is a good practice. In theory, they should be ignored anyway.

Each row is followed by the next immediately after without any separation other than the bits to complete the exact number of bytes, so glyphs are generated from the top down.

How Stunts renders text

The rendering of characters by Stunts does have a few quirks beside what is defined in the font format. The blitting routine appears to draw the pixels marked with 1 bits and leave the 0 bits as transparent. In other words, it does not implement a background colour. To create a background, Stunts first draws a rectangle and then blits the text on top of it. For this reason, while Stunts properly adjusts dialog boxes and menus to whatever dimensions the font is given, the rendering may produce artifacts if text is going to be written over text, such as when displaying a file list. In this case, going up and down the list requires erasing all the text and then blitting back on top, but Stunts assumes a font height. Therefore, if a taller font is used, the rectangles drawn before blitting will be shorter than the font and the lower pixels will remain and not be erased by the new text.

Another thing to consider is control codes. Stunts does not use ASCII standard control codes. Most code points result in the corresponding glyph just being drawn at the current Stunts cursor position and it advancing by the width of the character. The few code points that do have a special effects are the following:

  • 00h (NULL): In almost every case, this glyph won't be written because Stunts is written in C and uses ASCIIZ strings. As a result, a zero byte is understood as the end of the string. Still, if individual characters are to be written, Stunts has the capability of rendering the glyph.
  • 5Dh (closing square bracket - ]): In multi-line text, this character is not rendered because Stunts interprets it as an end-of-line mark. In single lines, it does display, though.
  • 5Bh (opening square bracket - [): This character is printed everywhere except in menus, where it is used for marking the beginning of a menu option. Stunts menus are stored in an array by number, so to recognise the part of the text that will link to each sub-menu or action, the opening bracket will mark the separation of one and the next, but not go to the next line. The closing bracket does the same thing, but moving to the next line.
  • 7Dh (closing curly bracket - }): This character is normally printed, but in multi-line text, it represents a half-height new line marker. It's used in menu boxes to mark the beginning of the menu option list. It is placed after the menu title and before the first menu option.
  • 40h (at symbol - @): This one is rendered almost everywhere except in text that is meant to carry a parameter or where a space has to be left for user input, such as when disabling the anti-piracy system or when entering your name for signing the scoreboard. In those cases, @ is placed to mark the position where the text is to be entered or inserted.

Stunts has a few extra special glyphs defined starting code point 80h, but these differ from font to font and don't appear to be used anywhere in the game.