Technical appendix · Home · Français
The text is decoded. See game-text.txt.
G3.CON rec 0x29 → 5-bit codes → glyph bytes → font atlas → pixels
Txt_Decode5Bit Vid_DrawGlyph
Text was invisible to earlier scans because it is neither ASCII nor byte-aligned glyph data.
byte = glyph index + 1.
| Index | Characters |
|---|---|
| 0-25 | a-z |
| 26-51 | A-Z |
| 52-61 | 0-9 |
| 62-74 | â ä à ç é ê ë è î ï ô ö ò (read from shapes) |
| 75, 76, 97, 98 | blank |
| 77-79 | û ù ü (read from shapes) |
| 80-96 | % " ' . , ; : ! ? / ( ) + - = ° ß (read from
shapes) |
Special bytes: 0x00 ends a string, 0x65
breaks a line, 0x4C (index 75) is the word space, which
Txt_DrawIndexed tests at 0x15913. Letter and
digit order, and all decoded French, confirm the map; accents and
punctuation come from their shapes.
Txt_Decode5Bit EA 0x11402
(verified)Far entry 0x84:0xBC2. Arguments: bits far pointer, sizes
far pointer, destination far pointer, index.
Txt_Read5Bits EA
0x114A2).n selects page[n-1]. Page 0 is
DS:429. Code 0x1E moves to page 1
(DS:446) and 0x1F to page 2
(DS:463), for the next code only.0x11478), one more code is read if at
least 5 bits remain in that byte (mask >= 0x10); it is
stored only if non-zero.Page 0 decodes to easrniutol.dc pmhvb\nfgé-'!,L, clearly
tuned for French.
| Bits | Sizes | Strings | Used by |
|---|---|---|---|
DS:1B06 |
DS:1BFD |
20 intro credits | Intro_OpShowCredit |
G3.CON rec 0x29 sub 0 |
rec 0x28 sub 0 |
1,589 game strings in 7 banks | MAUPITI1, through services 2 and 9 |
Credits include LANKHOR présente and
Copyright (c) 1990.
MAUPITI1.EXE adds these constants to the two pointers to
select a storage bank. Txt_DrawFromBank EA
0x1FD70 takes a text class and maps it
through a local array (5,3,1,2,7,4,6), so class numbers in
code differ from bank numbers. Each bank has its own string numbers, and
calls use them 1-based: Game_AddEnergy draws class 3,
string 37 + condition/20, which is bank 1 entry 36,
"Lessivé". Txt_DrawLinesBank12 EA 0x1F230
picks bank 1 or 2 directly.
| Bank | Class | Strings | Size-table bytes | Text bytes | Contents, from sampling them |
|---|---|---|---|---|---|
| 1 | 3 | 235 | 0x1D6 |
0x6CE |
verbs, menus, short messages |
| 2 | 4 | 80 | 0xA0 |
0x240 |
object names |
| 3 | 2 | 483 | 0x3C6 |
0x239E |
examine and object text |
| 4 | 6 | 102 | 0xCC |
0x270C |
long documents: chess games, grotto text, and some cipher-like strings |
| 5 | 1 | 103 | 0xCE |
0x104A |
story events |
| 6 | 7 | 135 | 0x10E |
0x6A0 |
questions and answers |
| 7 | 5 | 451 | 0x386 |
0x46C0 |
character dialogue |
The sizes add up exactly to records 0x28 (3,178 B) and
0x29 (45,410 B). Banks are word-aligned: bank 1's strings
total 1,741 B and bank 7's 18,111 B, each with one padding byte. No
string uses the invalid code 0.
Two aMpu-tagged descriptors, both 99 glyphs.
Vid_DrawGlyph uses whichever
wFontDescSeg:wFontDescOfs points to.
| Field | Meaning | DS:1522 (EA 0x1B942) |
DS:1F11 (EA 0x1C331) |
|---|---|---|---|
+0x4E u8 |
rows | 8 | 7 |
+0x5C u16 |
atlas | 110 | 110 |
+0x60 u16 |
row stride | 68 | 56 |
+0x62 u16 |
records {u16 bit offset, u16 ink width} |
654 | 502 |
+0x66 u16 |
advance table | 1050 | 898 |
+0x6A u16 |
nudge table | 1248 | 1096 |
| ink total / row capacity (bits) | 515 / 544 | 438 / 448 | |
| swapped atlas bytes | 544 | 392 |
advance == ink + 1 for 95 of 99 glyphs; the exceptions
are the blanks, with advances 6, 6, 6, 4.
Installers: Font_LoadCharset installs the 8-row font at
0x16F65; Font_SelectDescriptor EA
0x1734A installs the 7-row one.
Font_ByteswapBlock EA 0x1733C swaps every
aligned 16-bit word of the atlas once. After that, each row is a plain
MSB-first bitstream. To fetch a glyph, Vid_DrawGlyph takes
two bytes big-endian at offset >> 3, shifts left by
offset & 7, and samples the high byte, so at most 8
pixels per glyph. Some 8-row glyphs have ink widths up to 11; the code
cuts them at 8 pixels, and the atlas shows that.
Swap the words first. Reading the file as little-endian at the same byte offset is only right for even offsets, and gets 47 of the 7-row font's 99 glyphs wrong.
Vid_DrawGlyph EA 0x11590, per glyph:
AL - 1.nudge[index] to wCanvasX, and draws
at that position in wTextColorCur.advance[index]
(0x1164D-0x11663).So text is proportional. Vid_DrawString
EA 0x11858 draws until 0x00 or
0x65, copies wTextColor (DS:D4)
to wTextColorCur (DS:496), and adds a
caller-supplied extra letter spacing. Txt_DrawIndexed also
reads the advance table to measure text (0x158DF).
Callers:
service 9 → Txt_DrawIndexed EA 0x157C3 (also Intro_OpShowCredit x3)
service 2 → Vid_DrawTextLines EA 0x15A38 → Vid_DrawString → Vid_DrawGlyph
Txt_Decode5Bit at 0x15AC4
Vid_DrawStringEx EA 0x1188C → Vid_DrawGlyph
Txt_DrawIndexed calls Txt_Decode5Bit at
0x15822 and 0x158A7. Its 28-word block holds
the colour at +0xC, y at +0x18, x at
+0x1C, string buffer at +0x28, sizes at
+0x2C, bits at +0x30, and a 1-based string
number at +0. Vid_DrawTextLines advances by 10
per line (0x15C01, 0x15CCA).
The 0x4C-based wrapping and any drawing by
Txt_DrawIndexed beyond its decode and measuring steps have
not been traced in full.
Font_LoadCharset EA 0x16F04 is really the
intro (Intro_Run in Binary Ninja). It installs the 8-row
font, decodes rec 0x27 sub 2 to the page, copies it to
wPage2Seg, then runs the script at
DS:1C25:
| Opcode | Routine | Action |
|---|---|---|
0 |
0x17107 |
copy page |
4 |
0x17126 (IDA Font_LoadStrings) |
decode rec 0x27 sub n at x, y |
8 |
0x1716B |
service 0x12 |
0xC |
0x171A4 |
show credit string |
0x10 |
0x172D7 |
timed step (inferred) |
0xFF |
end check |
So record 0x27 holds intro pictures and music plus other
assets that MAUPITI1 loads at start (The two programs). It holds neither fonts
nor strings.