Disclosure: This article was generated with AI for the FrameSprite team, which has a commercial interest in FrameSprite. It received a separate AI editorial review against the cited sources. Executed synthetic fixtures are identified with their runtime and limits; no other editor, engine, production or performance test is implied.
Treat a binary BMFont v3 block size as the payload length after the block header. Advance past the one-byte type and four-byte size, then consume exactly that payload. Counting the header twice, or using an older layout rule, shifts every later field and can turn a filename into nonsense.
Key takeaway: Check the format version before reading blocks. A recognizable BMF signature is not enough to select a parser.
Trace one tiny file by byte position
The BMFont binary format defines the version byte and block envelope. The authored test contains an unknown block followed by a page-name block:
| Byte range | Authored value | Purpose |
|---|---|---|
| 0 to 3 | BMF and version 3 | Select the fixture profile |
| 4 to 8 | type 99, payload length 3 | Unknown block header |
| 9 to 11 | abc | Skip this payload |
| 12 to 16 | type 3, payload length 6 | Page-name block header |
| 17 to 22 | p.png plus a zero terminator | Page-name payload |
The unknown type is deliberate. It checks that a parser can skip a bounded block without guessing what its contents mean.
Validate before taking a slice
import struct
def blocks(data):
if data[:4] != b'BMF\x03':
raise ValueError('only binary v3 is accepted')
position = 4
while position < len(data):
if len(data) - position < 5:
raise ValueError('truncated block header')
kind, size = struct.unpack_from('<BI', data, position)
position += 5
if size > len(data) - position:
raise ValueError('truncated block payload')
yield kind, data[position:position + size]
position += size
Check lengths before slicing. Python slices can return a shorter value without an exception, so a slice alone is not a truncation check. Put a file-size limit outside this loop when reading untrusted input, and validate each recognized payload before allocating arrays from it.
Keep the version decision visible
The Cyotek parser project supports multiple descriptor forms, but selecting the correct form remains the consumer's responsibility. The UnrealBMFont changelog records bounded descriptor parsing and resource limits. These are useful implementation references; the short example above is not a complete secure font importer.
The recorded fixture result shows the two expected blocks, rejection of a shortened final payload, and rejection of a v2 header. Python 3.12.10 ran three assertions in the checker. The input contains the exact authored byte sequence. No font engine or texture loader was executed.
Diagnose the next boundary separately
A valid block envelope can still contain a missing page reference, invalid glyph rectangle or unsupported feature. Use a general atlas structural check as a model for reporting field-level errors, while retaining the correct binary-format rules here. If the page image itself is damaged, compare PNG chunk integrity. If the file is complete but a character is absent, perform the font coverage check.
Ship the descriptor with its assumptions
Record binary format v3 in the asset contract, preserve the original export, and report the failing byte position when validation stops. Do not convert a malformed descriptor into guessed metadata just to make the importer continue.
Editorial ownership
Prepared by the workflow team, which has a commercial relationship with this site. This guide was drafted with AI assistance; independent editorial review is recorded separately. Report a technical correction with the relevant input, tool version and observed result.