Files
Sylpheed/docs/re/structures/hud-glyph-quad.md
Sylpheed RE agent c77d51afc0 formats: correct the glyph class - a SECONDARY vtable of adjustor thunks
The first write-up called 0x820B2A64's 32 entries "methods" and the three xrefs
to it "construction sites". The DB's disassembly refutes both: every slot points
at a 16-byte adjustor thunk - lwz r11,-4(r3); subf r3,r11,r3; b <method> - which
is the PPC/MSVC thunk for a SECONDARY base subobject. So this is a secondary
vtable and the class uses multiple inheritance, and sub_823C3148, listed as a
constructor because it references the table, is actually the branch target of one
of the table's own thunks.

Following the 32 branch targets instead: 32 distinct addresses, 18 of them known
function starts, in two clusters (0x823c3xxx beside the thunks and 0x823e3xxx),
and NONE of them appears in any of the 1150 vtables the DB classified - so
nothing places this class in a named hierarchy, the disc's RTTI having no class
names. The two-cluster split is noted as a reading rather than a measurement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-24 10:16:12 +00:00

101 lines
3.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# The HUD's glyph quad — vtable `0x820B2A64`
**Status: ✅ `CONFIRMED` for the object layout and the atlas size**, read live off
a Stage 02 run and checked against itself. 🟡 the class has no name (RTTI carries
none disc-wide).
## How it was reached
[`mission-objective-counter.md`](mission-objective-counter.md) found that the
words moving with `REMAINING OB` are four ASCII digits and four guest pointers.
Following the pointers lands on four objects with an identical shape, whose first
word is `0x820B2A64`.
## It is a SECONDARY vtable — 32 adjustor thunks
Read out of guest memory: 32 consecutive code pointers in `0x823c43b0 …
0x823c45a0`, terminated by `0xfffffffc`.
🔴 **Corrected 2026-08-24, same session.** The first write-up called these "32
methods" and the three xrefs to the table "three construction sites". Both were
wrong, and `sylpheed.db`'s disassembly says why — every slot points at a
**16-byte adjustor thunk**:
```
lwz r11, -4(r3) ; the this-adjustment stored just before the object
subf r3, r11, r3 ; fix up `this`
b <the real method>
.long 0
```
That is the classic PPC/MSVC thunk for a **secondary base subobject**, so
`0x820B2A64` is a *secondary* vtable and the class uses multiple inheritance.
`sub_823C3148`, which the first write-up listed as a constructor because it
*references* the table, is in fact the branch target of one of the table's own
thunks.
**Where the real methods live.** Taking the 32 branch targets: 32 distinct
addresses, **18** of them known function starts in `sylpheed.db`, in two clusters
`0x823c3xxx` (beside the thunk block itself) and `0x823e3xxx`. The rest land on
further stubs immediately after the block. **None** of the 32 appears in any of
the 1 150 vtables the DB classified, so nothing places this class in a named
hierarchy; the disc's RTTI carries no class names.
The two clusters are what a derived class looks like — its own overrides next to
its own thunks, and inherited implementations elsewhere — but that is a reading,
not a measurement.
## The instance
```
-0x0c f32 1280.0 ) the design space, immediately before the object
-0x08 f32 720.0 )
+0x00 u32 0x820B2A64 vtable
+0x1c f32 34.0 width in pixels
+0x20 f32 42.0 height in pixels
+0x30 u32 0xffffffff vertex 0 colour
+0x34 f32 u u
+0x38 f32 v v
+0x48 … vertex 1 (+0x18 stride)
+0x60 … vertex 2
+0x78 … vertex 3
```
Measured on one digit glyph:
| vertex | colour | u | v |
|---|---|---|---|
| 0 | `0xffffffff` | 0.5477 | 0.5013 |
| 1 | `0xffffffff` | 0.5742 | 0.5013 |
| 2 | `0xffffffff` | 0.5742 | 0.5560 |
| 3 | `0xffffffff` | 0.5477 | 0.5560 |
Four corners of an axis-aligned rectangle, in order, all untinted — a textured
quad.
## ✅ The atlas is 1280 × 768, and that is a check rather than a guess
The UV rectangle is 0.0265 × 0.0547. Multiply by **1280 × 768**:
```
0.0265 × 1280 = 33.9 -> the width stored at +0x1c is 34.0
0.0547 × 768 = 42.0 -> the height stored at +0x20 is 42.0
```
Two independent fields of the same object agree to within a rounding step, which
is what makes the atlas size a measurement instead of a plausible number.
## ⚠️ These objects churn
The four pointers next to the counter swap as the digit changes, and re-reading
one of the *old* targets a minute later returned unrelated data — the object had
been recycled. Anything read here is a snapshot of a live pool, not a stable
address.
## What this is for
It gives the port a decoded HUD text primitive: a glyph is a quad with a pixel
size and four UVs into a 1280×768 atlas, drawn by a class with 32 virtual methods
and three constructors. And it explains why the objective counter was so easy to
find in RAM — the value sits inside the widget that renders it.