summaryrefslogtreecommitdiffstats
path: root/vgui_surface030_vtable_identification.md
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-09-12 19:04:22 +0200
committersalmonik <salmonik@tuta.io>2026-09-12 19:04:22 +0200
commit210f3a12ee1d56750d593b4c404a0683f810aa3e (patch)
tree926a136c0366616077b03c2e48ffba5af7312d08 /vgui_surface030_vtable_identification.md
parentinit (diff)
downloadhl2h-210f3a12ee1d56750d593b4c404a0683f810aa3e.tar.gz
hl2h-210f3a12ee1d56750d593b4c404a0683f810aa3e.zip
ISurface overlay working: PTEx draw path, -Werror, menu states, bind-driven features
Diffstat (limited to 'vgui_surface030_vtable_identification.md')
-rw-r--r--vgui_surface030_vtable_identification.md227
1 files changed, 227 insertions, 0 deletions
diff --git a/vgui_surface030_vtable_identification.md b/vgui_surface030_vtable_identification.md
new file mode 100644
index 0000000..7adbda8
--- /dev/null
+++ b/vgui_surface030_vtable_identification.md
@@ -0,0 +1,227 @@
1# ISurface VGUI_Surface030 VTable Identification
2
3## Binary Info
4- **File**: `/home/salmon/.local/share/Steam/steamapps/common/Half-Life 2/bin/vguimatsurface.so`
5- **Format**: ELF 32-bit LSB shared object, Intel i386, **not stripped**
6- **Load bias**: `0xe2dee000`
7- **VTable location**: `.data.rel.ro` at file offset `0x246e60`, virtual address `0x247e60`
8- **Total vtable entries**: 179
9- **Typeinfo**: CMatSystemSurface typeinfo at vaddr `0x24a500`, referenced at vtable[-1]
10
11## Methodology
12
13The vtable was located by finding the CMatSystemSurface `__si_class_type_info` object at vaddr `0x24a500` (identified by its `__type_name` pointing to `"17CMatSystemSurface"` at `0x1ec4ec`), then finding where that address is stored in `.data.rel.ro` as a vtable typeinfo pointer. The vtable entries (consecutive `R_386_RELATIVE` relocations in `.data.rel.ro` pointing into `.text`) start immediately after the typeinfo pointer.
14
15Each function was identified by disassembling the function body and matching behavioral patterns against the ISurface.h header from the project.
16
17## VTable Layout (Itanium ABI)
18
19```
20vtable[-2] = offset-to-top = 0x00000000 (vaddr 0x247e58)
21vtable[-1] = typeinfo* = 0x0024a500 (vaddr 0x247e5c) → CMatSystemSurface
22vtable[ 0] = func ptr = 0x0006c010 (vaddr 0x247e60) → Connect (IAppSystem)
23...
24vtable[178]= 0x0006b250
25```
26
27---
28
29## Identified Function Slot Numbers
30
31### 1. DrawSetTextFont(HFont) → **Slot 17**
32- **File offset**: `0x00069360`
33- **Runtime**: `0xe2e57360` (load_bias + 0x69360)
34- **Member offset**: `this+0x290`
35
36```asm
3700069360: push %ebp
3800069361: mov %esp,%ebp
3900069363: mov 0x8(%ebp),%eax ; eax = this
4000069366: mov 0xc(%ebp),%edx ; edx = HFont
4100069369: mov %edx,0x290(%eax) ; this->m_hCurrentFont = font
420006936f: pop %ebp
4300069370: ret
44```
45
46**Evidence**: Trivially stores a single int (HFont) at `this+0x290`. Cross-validated: slot 22 (DrawPrintText) reads from `0x290(%esi)` to get the current font.
47
48---
49
50### 2. DrawSetTextColor(int r, int g, int b, int a) → **Slot 18**
51- **File offset**: `0x00069380`
52- **Runtime**: `0xe2e57380` (load_bias + 0x69380)
53- **Member offsets**: `this+0x28` (R), `this+0x29` (G), `this+0x2a` (B), `this+0x2b` (A)
54
55```asm
5600069380: push %ebp
5700069381: pxor %xmm0,%xmm0
5800069385: mov %esp,%ebp
590006938d: mov 0x8(%ebp),%edi ; edi = this
6000069390: cvtsi2ss 0x18(%ebp),%xmm0 ; xmm0 = (float)a
6100069395: mov 0xc(%ebp),%ebx ; ebx = r
6200069398: mulss 0x10(%edi),%xmm0 ; xmm0 *= this->m_flAlphaMultiplier
630006939d: movzbl 0x28(%edi),%ecx ; load current R
64000693a1: cvttss2si %xmm0,%esi ; esi = (int)(a * alphaMul)
65000693a5: cmp %ebx,%ecx ; if (currentR == newR)
66000693a7: je 693d8 ; skip vtable call
67000693a9: mov (%edi),%ecx ; vtable ptr
68000693af: call *0x60(%ecx) ; call vtable[24] (notification callback)
69000693b6: mov %bl,0x28(%edi) ; this->textColor.r = r
70000693bc: mov al,0x29(%edi) ; this->textColor.g = g (from 0x10(%ebp))
71000693c3: mov al,0x2a(%edi) ; this->textColor.b = b (from 0x14(%ebp))
72000693c8: mov al,0x2b(%edi) ; this->textColor.a = a (clamped/int)
73000693d2: ret
74```
75
76**Evidence**: Stores 4 consecutive bytes (RGBA) at `this+0x28..0x2b`. The alpha is multiplied by `m_flAlphaMultiplier` at `this+0x10` before clamping. If the red component changed, it calls a notification callback via vtable[0x60/4=24]. Cross-validated: DrawSetColor (slot 10) stores at `this+0x24..0x27` (shape color, different location).
77
78---
79
80### 3. DrawSetTextPos(int x, int y) → **Slot 20**
81- **File offset**: `0x00069440`
82- **Runtime**: `0xe2e57440` (load_bias + 0x69440)
83- **Member offsets**: `this+0x2c` (x), `this+0x30` (y)
84
85```asm
8600069440: push %ebp
8700069441: mov %esp,%ebp
8800069443: mov 0x8(%ebp),%eax ; eax = this
8900069446: mov 0xc(%ebp),%edx ; edx = x
9000069449: mov %edx,0x2c(%eax) ; this->m_nTextX = x
910006944c: mov 0x10(%ebp),%edx ; edx = y
920006944f: mov %edx,0x30(%eax) ; this->m_nTextY = y
9300069452: pop %ebp
9400069453: ret
95```
96
97**Evidence**: Stores 2 ints at `this+0x2c` and `this+0x30`. Cross-validated: DrawGetTextPos (slot 21) at `0x69460` reads from the same offsets (`0x2c(%eax)` and `0x30(%eax)`).
98
99---
100
101### 4. DrawPrintText(const wchar_t* text, int textLen, FontDrawType_t drawType) → **Slot 22**
102- **File offset**: `0x00072980`
103- **Runtime**: `0xe2e60980` (load_bias + 0x72980)
104
105```asm
10600072980: push %ebp
10700072981: mov %esp,%ebp
10800072983: push %edi
10900072984: push %esi
11000072985: push %ebx
11100072986: call 624a9 ; __x86.get_pc_thunk (PIC setup)
1120007298b: add $0x1fe675,%ebx
11300072991: sub $0x9c,%esp ; large stack frame
11400072997: mov 0xc(%ebp),%edi ; edi = text (wchar_t*)
115000729a5: mov 0x8(%ebp),%esi ; esi = this
116000729a8: test %edi,%edi ; if (!text) return
117000729b0: mov 0x290(%esi),%eax ; load current font (HFont)
118000729b6: test %eax,%eax ; if (!font) return
119000729be: mov 0x8(%esi),%edx ; base x
120000729c4: add 0x2c(%esi),%edx ; + text offset x
121000729db: push %eax ; font
122000729dc: push %esi ; this
123000729dd: call *0x114(%edx) ; vtable[0x114/4=69] → GetFontTall or similar
124```
125
126**Evidence**: Large function (~800+ bytes) that:
1271. Reads the current font from `this+0x290` (matching DrawSetTextFont's store)
1282. Computes render position from base coordinates + text offset
1293. Calls vtable[0x114] (a font metrics function)
1304. Contains complex text layout/rendering logic with stack frame of 0x9c bytes
1315. References the `FinishDrawing` string indirectly through the rendering pipeline
132
133---
134
135### 5. CreateFont() → **Slot 66**
136- **File offset**: `0x0006a2d0`
137- **Runtime**: `0xe2e582d0` (load_bias + 0x6a2d0)
138
139```asm
1400006a2d0: push %ebp
1410006a2d1: mov %esp,%ebp
1420006a2d3: push %ebx
1430006a2d4: call 624a9 ; __x86.get_pc_thunk
1440006a2d9: add $0x206d27,%ebx
1450006a2df: sub $0x4,%esp
1460006a2e2: call 1693b0 ; → get CFontManager* (PIC global lookup)
1470006a2e7: sub $0xc,%esp
1480006a2ea: push %eax ; pass font manager
1490006a2eb: call 16a200 ; → CFontManager::CreateFont() → returns HFont
1500006a2f0: mov -0x4(%ebp),%ebx
1510006a2f3: leave
1520006a2f4: ret ; return HFont in eax
153```
154
155**Evidence**: Calls `0x1693b0` which returns a pointer to the global CFontManager singleton (PIC-computed from GOT), then calls `0x16a200` which allocates a new font entry and returns its HFont handle. Simple wrapper — no arguments besides `this`.
156
157---
158
159### 6. AddGlyphSetToFont(HFont font, const char* windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin, int nRangeMax) → **Slot 67**
160- **File offset**: `0x0006a300`
161- **Runtime**: `0xe2e58300` (load_bias + 0x6a300)
162
163```asm
1640006a300: push %ebp
1650006a301: mov %esp,%ebp
1660006a303: push %ebx
1670006a304: call 624a9 ; __x86.get_pc_thunk
1680006a309: add $0x206cf7,%ebx
1690006a30f: sub $0x4,%esp
1700006a312: call 1693b0 ; → get CFontManager*
1710006a317: sub $8,%esp
1720006a31a: push 0x2c(%ebp) ; arg 9: nRangeMax
1730006a31d: push 0x28(%ebp) ; arg 8: nRangeMin
1740006a320: push 0x24(%ebp) ; arg 7: flags
1750006a323: push 0x20(%ebp) ; arg 6: scanlines
1760006a326: push 0x1c(%ebp) ; arg 5: blur
1770006a329: push 0x18(%ebp) ; arg 4: weight
1780006a32c: push 0x14(%ebp) ; arg 3: tall
1790006a32f: push 0x10(%ebp) ; arg 2: windowsFontName (const char*)
1800006a332: push 0x0c(%ebp) ; arg 1: font (HFont)
1810006a335: push %eax ; font manager
1820006a336: call 16a550 ; → CFontManager::SetFontGlyphSet()
1830006a33b: mov -0x4(%ebp),%ebx
1840006a33e: leave
1850006a33f: ret
186```
187
188**Evidence**: Pushes all 9 user-visible arguments (HFont + const char* + 7 ints) plus the font manager pointer onto the stack and calls `0x16a550`. That inner function:
189- Multiplies HFont by `0x3c` (60 bytes per font entry) to index into the font array
190- Calls `0x169ec0` to resolve the font name string
191- Calls `0x16a220` to configure the font with FreeType parameters
192
193Note: The ISurface.h header in the project names this `SetFontGlyphSet`, but older Source SDK versions (VGUI_Surface030 from HL2) call it `AddGlyphSetToFont`. Same function, different name.
194
195---
196
197## Summary Table
198
199| Slot | Function | File Offset | Runtime Address | Member Offset |
200|------|----------|-------------|-----------------|---------------|
201| 17 | `DrawSetTextFont(HFont)` | `0x00069360` | `0xe2e57360` | `this+0x290` |
202| 18 | `DrawSetTextColor(int,int,int,int)` | `0x00069380` | `0xe2e57380` | `this+0x28..0x2b` |
203| 20 | `DrawSetTextPos(int,int)` | `0x00069440` | `0xe2e57440` | `this+0x2c, +0x30` |
204| 22 | `DrawPrintText(wchar_t*,int,FontDrawType_t)` | `0x00072980` | `0xe2e60980` | reads `this+0x290` |
205| 66 | `CreateFont()` | `0x0006a2d0` | `0xe2e582d0` | returns HFont |
206| 67 | `AddGlyphSetToFont(HFont, const char*, int×7)` | `0x0006a300` | `0xe2e58300` | calls `0x16a550` |
207
208## Related Slots
209
210| Slot | Function | Evidence |
211|------|----------|----------|
212| 10 | `DrawSetColor(int,int,int,int)` | stores at `this+0x24..0x27` (shape color) |
213| 11 | `DrawSetColor(Color_t)` | extracts packed int, stores at `this+0x24..0x27` |
214| 19 | `DrawSetTextColor(Color_t)` | stores at `this+0x28..0x2b` (text color, via packed Color_t) |
215| 21 | `DrawGetTextPos(int&,int&)` | reads from `this+0x2c, +0x30` (inverse of DrawSetTextPos) |
216
217## CMatSystemSurface Member Layout (inferred)
218
219| Offset | Type | Field |
220|--------|------|-------|
221| `+0x00` | `void**` | vtable pointer |
222| `+0x10` | `float` | `m_flAlphaMultiplier` |
223| `+0x24` | `uint8[4]` | `drawColor` (RGBA for shapes) |
224| `+0x28` | `uint8[4]` | `textColor` (RGBA for text) |
225| `+0x2c` | `int` | `m_nTextX` |
226| `+0x30` | `int` | `m_nTextY` |
227| `+0x290` | `HFont` | `m_hCurrentFont` |