Layout and glyph data
11. Layout mathematics
Section titled “11. Layout mathematics”11.1 Glyph cell and text speed
Section titled “11.1 Glyph cell and text speed”Every dialogue message opens with a 03 20 dimensions record and a 07 1x speed
record. Across a full English packet file and a full Japanese one, each is
completely uniform within its region and differs between them
confirmed:
| Region | 03 20 payload |
Cell (U8.8) | 07 10 payload |
Speed |
|---|---|---|---|---|
| English | 33 0B 66 16 |
11.20 × 22.40 |
00 03 / 00 04 |
3.00, then 4.00 |
| Japanese | 66 0E 00 10 |
14.40 × 16.00 |
00 07 |
7.00 |
The Japanese cell is wider and much shorter — nearly square, as full-width kana
and kanji need — where the English cell is narrow and tall. Note also that the
trailing 33 and 66 bytes in the English payload are just the fractional parts
of 11.20 and 22.40; they are not fixed markers, and the Japanese payload uses
66 and 00.
The renderer derives its advances from that cell:
full_advance = (width * 32) >> 12 ; one whole cellline_height = (height * 32) >> 12space_narrow = (width * 12) >> 12 ; English builds only| Region | full_advance |
line_height |
Space command 01 00 advances by |
|---|---|---|---|
| English | 22 |
44 |
8 |
| Japanese | 28 |
32 |
28 |
That last column is a genuine engine difference rather than a data one, and it is
visible in the jump-table handlers
(§6): the English builds compute
the extra * 12 >> 12 term and use it for the space command, so a space is about
three quarters of a cell. The Japanese build never computes that term at all —
its space handler advances by full_advance, so a space occupies one whole
character cell, which is what Japanese typesetting expects. confirmed
11.2 Vertical placement
Section titled “11.2 Vertical placement”Line Y coordinates come from the 04 20 records, one per line, and the
name-plate offset from u16[6] of the 0E 4E record. Observed values
confirmed:
| Lines | English Y | English plate | Japanese Y | Japanese plate |
|---|---|---|---|---|
| 1 | 190 |
118 |
184 |
88 |
| 2 | 168, 208 |
96 |
168, 216 |
88 |
| 3 | 136, 176, 216 |
64 |
not observed | not observed |
Two differences fall out. Line spacing is 48 in Japanese and 40 in English,
and the Japanese name plate sits at a fixed 88 for both line counts seen,
where the English plate tracks the first line at first_y - 72. No three-line
Japanese message appears in the sample examined, so the Japanese column stops at
two.
The English series is a clean fit inferred:
n = number_of_lines ; 1, 2 or 3first_y = -5*n^2 - 7*n + 202y(i) = first_y + i * 40plate_y = first_y - 72Two Japanese data points are not enough to fit a curve, so the observed values above should be used literally rather than extrapolated.
11.3 Horizontal centring
Section titled “11.3 Horizontal centring”The advance is not a flat ratio — it scales with the cell, and it truncates per glyph. ’s measuring pass computes, for each glyph confirmed:
003164C8 andi $v0, $s2, 0xffff ; s2 = dimensions width word003164D0 mult $v1, $v0, $v1 ; width_word * spacing_byte003164DC sra $v0, $v1, 0xc ; >> 12, truncated per glyph003164E4 addu $s3, $s3, $v0 ; pen_x += advanceadvance(c) = (width_word * spacing[c]) >> 12line_width = sum(advance(c) for c in visible_text)x = -(line_width // 2)spacing[c] comes from a per-glyph width table selected by text style and
attribute bits, and defaults to 0x20 when the font carries no table. That
default is exactly why Japanese is monospaced: it has no width table, so every
glyph advances (0x0E66 * 32) >> 12 = 28. Measured across a Japanese file the
advance is 28.00 px per character on every line, without exception.
| Region | width_word |
Advance | Reproduces the stored X |
|---|---|---|---|
| English | 0x0B33 |
proportional, spacing 10–30 | 53 / 56 lines |
| Japanese | 0x0E66 |
uniform 28 px | 20 / 20 lines |
The three English lines that do not match all belong to one message, and that
same message also declares a glyph count of 48 where it draws 74. A file that is
internally inconsistent in two ways at once looks edited by tooling carrying
these very bugs rather than authored that way, so it is weak evidence about the
format. A flat spacing × 2/3 approximation, which is what the older tooling
used, happens to land within a pixel or two for English because
2867/4096 = 0.700 against 0.667 is largely cancelled by the per-glyph
truncation. It is badly wrong for any other cell size.
Each line carries its own 04 20 record, so a three-line box holds three
independently centred X values. The alternative is to let the engine do it:
setting flags & 3 in the 0F 25 header makes _CbSentence measure each line
and overwrite these X fields itself
(§7). Neither sample file uses
that path — every 0F 25 in both carries flags = 0.
12. Ruby and furigana
Section titled “12. Ruby and furigana”There is no ruby opcode. Ruby is a composition built on the generic style stack (§6.1): save the style, shrink the glyphs, jump to an absolute position above the baseline, draw the annotation, then restore. The full sequence is:
09 10 BC 00 ; StyleSave: pen X/Y, width, height, speed03 20 33 04 66 0F ; Dimensions -> 4.20 x 15.40 (vs 11.20 x 22.40 body)04 20 <x> <y> ; Position: centred over the base run, y - 2802 20 6A 69 5F 80 ; Colour, matching the base text07 10 00 00 ; TextSpeed 0 -> annotation appears instantly<annotation glyphs>09 11 BC 00 ; StyleRestore: same mask<base text glyphs>The annotation is emitted before the base text and positioned absolutely, so
the base run’s own advance is unaffected. Ruby X is
base_center - annotation_width / 2, with the annotation measured at a scale of
4/11 to match its reduced cell width, and Y is line_y - 28.
13. Embedded glyph data
Section titled “13. Embedded glyph data”A file may append glyphs of its own after its packets, for characters the resident font does not have. Two sections can follow the packet data, in this order:
| Section | Starts at | Size | Contents |
|---|---|---|---|
| glyph tiles | packet_data_size |
glyph_data_size |
32×32 tiles at 4 bits per pixel, 512 bytes each, stacked in order. The size is always a whole multiple of 512. |
| glyph widths | packet_data_size + glyph_data_size |
ceil(tiles/4)*4 |
One advance-width byte per tile, rounded up to a 4-byte boundary. Present in the English files examined, absent from the Japanese ones. confirmed |
13.1 Tile addressing
Section titled “13.1 Tile addressing”Tile i is addressed by the character word 0x8010 + i. Reading that back
through §5: b1 = 0x80 sets the attribute bit
and leaves the page at 0, and b0 = 0x10 + i is the slot. So the 0x80
attribute bit is what selects the file’s own glyph block instead of the resident
font.
Both payload files agree, exactly and independently confirmed:
| File | glyph_data_size |
Tiles | Distinct 0x80 words |
Range | Unreferenced |
|---|---|---|---|---|---|
| English dialogue | 34304 |
67 |
67 |
0x8010–0x8052 |
0 |
| Japanese dialogue | 4608 |
9 |
8 |
0x8010–0x8017 |
1 |
| Japanese save names | 9728 |
19 |
— | — | — |
The English case is the strongest: 67 tiles, 67 distinct glyph words, contiguous from the base, none unreferenced and none pointing past the block.
13.2 Decoding the tiles
Section titled “13.2 Decoding the tiles”p0 = byte & 0x0F # low nibble firstp1 = (byte >> 4) & 0x0Fpixel = nibble * 17 # expand 4-bit to 8-bit greytiles = reshape(pixels, (h, 32)) # 32 x 32 tiles, stacked verticallyThe nibble histogram of a real block is strongly bimodal at 0 and 15 with
populated mid-tones, which is what antialiased glyph coverage looks like and is
not what packet data or padding looks like.
13.3 The width table
Section titled “13.3 The width table”In the English file, 68 bytes follow the tile block for its 67 tiles. Every value lies between 27 and 30, and 58 of the 67 are exactly 30.
Those are advance widths in the same units the resident font uses. The resident English spacing table runs 10–30, its single most common value is 30, and its next three are 29, 28 and 27 — the same top of the range, in the same order. The embedded glyphs are wide characters, so they cluster at the maximum.
inferredThat the Japanese files have no such table fits: their glyphs are full-width and
uniform, so there is nothing per-glyph to record. It also explains why the
Japanese header can omit glyph_data_end — with no section after the tiles,
[0x0C] + [0x08] is simply end-of-file.