OBD:TSFT
|
|
TSFT contains some info about the font at the top and then all of the actual pixel data for the glyphs of a font at a given size (as part of the font family, TSFF, that links to it). However the glyph data is a packed sequence of pixels for every character in the font; the TSGA(s) linked from TSFT must be read to know the boundaries of each character's pixels and the character code that they represent. To learn about the text encoding systems used for different versions of Oni, see OBD:Text encoding.
PC implementation
Here is the format used by Windows and Mac Oni.
| Offset | Type | Raw Hex | Value | Description |
|---|---|---|---|---|
| 0x000 | res_id | 01 03 00 00 | 3 | 00003-.TSFT |
| 0x004 | lev_id | 01 00 00 00 | 0 | level 0 |
| 0x008 | pad[6] | AD DE | padding | |
| 0x00E | uint16 | 07 00 | 7 | font size in points |
| 0x010 | uint32 | 01 00 00 00 | 1 | font style; the following options are possible:
|
| 0x014 | int16 | 09 00 | 9 | ascending height |
| 0x016 | int16 | 02 00 | 2 | descending height |
| 0x018 | int16 | 02 00 | 2 | leading height |
| 0x01a | int16 | 02 00 | 2 | leading width; unused |
| Room for 256 TSGA links (only the first one is typically used for Western languages) | ||||
| 0x01C | link | 01 04 00 00 | 4 | link to 00004-.TSGA (glyph array for character codes 0-255) |
| 0x020 | link | 00 00 00 00 | 0 | unused |
| ... | ... | ... | ... | ... |
| 0x418 | link | 00 00 00 00 | 0 | unused |
| Pixel data for the glyphs (8-bit intensities for four pixels at a time, stored as 32-bit integers) | ||||
| 0x41C | uint32 | 4F 0E 00 00 | 3663 | array size (one element has a length of 4 bytes) |
| 0x420 | uint32 | FF FF FF FF | 255,255,255,255 | the first 4 pixels (all transparent) of the first used glyph |
| ... | ... | ... | ... | the rest of the pixel data |
In the Windows/Mac data, a glyph is basically a grayscale bitmap with its dimensions specified by TSGA. The pixel data block stores each pixel as a single byte, giving it 256 degrees of opacity, which in theory allows for rather subtle antialiasing. In practice, for the smallest font size there is no antialiasing at all in the pixel data, as small fonts need to be crisp to be readable, and larger font sizes actually use only 17 shades of opacity – 0x00–0x0F and 0xFF – which the engine further posterizes to 4-bit (16 shades) when rendering.
The size of the total pixel data in PC TSFT is derived easily by multiplying the array size by the element size (4 bytes). This math is considerably more complicated on the PS2 side where the array size at 0x41C has been replaced by a raw offset.
Some salient points which contrast with the PS2 format below:
- 1. The PC TSFTs store pixel data in a variable-size array (number of elements announced at 0x41C, then 4 bytes per element).
- 2. A whole byte is allotted for every pixel.
- 3. The pixel format is what TXMP calls A8, namely 256 shades of opacity. But the numbers run in increasing order of transparency such that 0xFF is fully transparent and 0x00 is fully opaque.
- 4. The pixels (packed 4-by-4 as little-endian uint32s) are stored in row-major order: left to right, top to bottom. But the pixel rows for a given glyph can span across any portion of the 4-byte array elements.
- 5. Pixel data for a new glyph must always start at the first byte of an array element because glyph data in a PC TSGA is indexed by TSFT array element, not by pixel. If a glyph has 33 pixels (33 bytes) then it will require 36 bytes of storage on disk; the actual pixel data will span 8 whole array elements plus 1 byte of a 9th element, with the remaining 3 bytes of the 9th element padded out (typically with "AD DE AD").
PS2 implementation
The PS2 TSFT differs at this point, with this one field ending the instance data:
| Offset | Type | Raw Hex | Value | Description |
|---|---|---|---|---|
| 0x41C | offset | 20 00 00 00 | 0x00000020 | offset of the pixel data in the .raw file |
In the PS2 implementation, glyphs are still grayscale bitmaps but the storage is much more compact, with only 2 bits per pixel (i.e., 4 pixels per byte).
The total size of the pixel data used by a font is not stored in the PS2 TSFT, nor is it implied (as in the PC version where we can simply multiply TSFT's array size by 4 bytes); it must be computed from the TSGA by finding the maximum value of (start element index + bitmap width × bitmap height) across all 256 entries — that value is the offset of the pixel data for a glyph in quarter-byte increments — then dividing by 4 and rounding up to the next whole number to get the total bytes occupied in the .raw file. (TSGA's start element index values are not consecutive in any way, so you can't simply look at the last one to get the highest value.)
Key differences from the PC format:
- 1. Instead of storing pixel data in a variable-size array, PS2 TSFTs store the pixel data in the .raw file and use the field at 0x41C to store the raw offset.
- 2. PS2 fonts use 2 bits per pixel.
- 3. The pixel format is I2, allowing only 4 degrees of opacity. A single opaque pixel in a row of four will thus be stored as 0x03, 0x0C, 0x30 or 0xC0. Pixels are stored little-endian within each byte, meaning the first (leftmost) pixel is in the lowest 2 bits of the byte.
- 4. The pixel storage is still in row-major order but it is inside the .raw file.
- 5. The glyph data is not byte-aligned at all: each new glyph can start at any quarter-way point through a byte – the indexation for each glyph in the PS2 TSGA is by pixel, i.e. by quarter-byte, not by whole byte.
P.S.: The template checksum differs for PS2, not because the instances' fields differ in number or size (they're still six 16-bit fields followed by two 32-bit fields), but because Bungie's checksum calculation included the names of the struct members, and presumably Rockstar renamed the field at 0x0C when they repurposed it.
| ONI BINARY DATA |
|---|
| TSFL << Other file types >> TSGA |
| TSFT : Font |
| Interface file |
