summaryrefslogtreecommitdiffstats
path: root/vgui_surface030_vtable_identification.md
blob: 7adbda88b90abdf28081f5275f8d7f6ed7c6990d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# ISurface VGUI_Surface030 VTable Identification

## Binary Info
- **File**: `/home/salmon/.local/share/Steam/steamapps/common/Half-Life 2/bin/vguimatsurface.so`
- **Format**: ELF 32-bit LSB shared object, Intel i386, **not stripped**
- **Load bias**: `0xe2dee000`
- **VTable location**: `.data.rel.ro` at file offset `0x246e60`, virtual address `0x247e60`
- **Total vtable entries**: 179
- **Typeinfo**: CMatSystemSurface typeinfo at vaddr `0x24a500`, referenced at vtable[-1]

## Methodology

The 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.

Each function was identified by disassembling the function body and matching behavioral patterns against the ISurface.h header from the project.

## VTable Layout (Itanium ABI)

```
vtable[-2] = offset-to-top = 0x00000000  (vaddr 0x247e58)
vtable[-1] = typeinfo*    = 0x0024a500  (vaddr 0x247e5c) → CMatSystemSurface
vtable[ 0] = func ptr     = 0x0006c010  (vaddr 0x247e60) → Connect (IAppSystem)
...
vtable[178]= 0x0006b250
```

---

## Identified Function Slot Numbers

### 1. DrawSetTextFont(HFont) → **Slot 17**
- **File offset**: `0x00069360`
- **Runtime**: `0xe2e57360` (load_bias + 0x69360)
- **Member offset**: `this+0x290`

```asm
00069360:  push   %ebp
00069361:  mov    %esp,%ebp
00069363:  mov    0x8(%ebp),%eax       ; eax = this
00069366:  mov    0xc(%ebp),%edx       ; edx = HFont
00069369:  mov    %edx,0x290(%eax)     ; this->m_hCurrentFont = font
0006936f:  pop    %ebp
00069370:  ret
```

**Evidence**: Trivially stores a single int (HFont) at `this+0x290`. Cross-validated: slot 22 (DrawPrintText) reads from `0x290(%esi)` to get the current font.

---

### 2. DrawSetTextColor(int r, int g, int b, int a) → **Slot 18**
- **File offset**: `0x00069380`
- **Runtime**: `0xe2e57380` (load_bias + 0x69380)
- **Member offsets**: `this+0x28` (R), `this+0x29` (G), `this+0x2a` (B), `this+0x2b` (A)

```asm
00069380:  push   %ebp
00069381:  pxor   %xmm0,%xmm0
00069385:  mov    %esp,%ebp
0006938d:  mov    0x8(%ebp),%edi       ; edi = this
00069390:  cvtsi2ss 0x18(%ebp),%xmm0   ; xmm0 = (float)a
00069395:  mov    0xc(%ebp),%ebx       ; ebx = r
00069398:  mulss  0x10(%edi),%xmm0     ; xmm0 *= this->m_flAlphaMultiplier
0006939d:  movzbl 0x28(%edi),%ecx      ; load current R
000693a1:  cvttss2si %xmm0,%esi        ; esi = (int)(a * alphaMul)
000693a5:  cmp    %ebx,%ecx            ; if (currentR == newR)
000693a7:  je     693d8                ;   skip vtable call
000693a9:  mov    (%edi),%ecx          ; vtable ptr
000693af:  call   *0x60(%ecx)          ; call vtable[24] (notification callback)
000693b6:  mov    %bl,0x28(%edi)       ; this->textColor.r = r
000693bc:  mov    al,0x29(%edi)        ; this->textColor.g = g  (from 0x10(%ebp))
000693c3:  mov    al,0x2a(%edi)        ; this->textColor.b = b  (from 0x14(%ebp))
000693c8:  mov    al,0x2b(%edi)        ; this->textColor.a = a  (clamped/int)
000693d2:  ret
```

**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).

---

### 3. DrawSetTextPos(int x, int y) → **Slot 20**
- **File offset**: `0x00069440`
- **Runtime**: `0xe2e57440` (load_bias + 0x69440)
- **Member offsets**: `this+0x2c` (x), `this+0x30` (y)

```asm
00069440:  push   %ebp
00069441:  mov    %esp,%ebp
00069443:  mov    0x8(%ebp),%eax       ; eax = this
00069446:  mov    0xc(%ebp),%edx       ; edx = x
00069449:  mov    %edx,0x2c(%eax)      ; this->m_nTextX = x
0006944c:  mov    0x10(%ebp),%edx      ; edx = y
0006944f:  mov    %edx,0x30(%eax)      ; this->m_nTextY = y
00069452:  pop    %ebp
00069453:  ret
```

**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)`).

---

### 4. DrawPrintText(const wchar_t* text, int textLen, FontDrawType_t drawType) → **Slot 22**
- **File offset**: `0x00072980`
- **Runtime**: `0xe2e60980` (load_bias + 0x72980)

```asm
00072980:  push   %ebp
00072981:  mov    %esp,%ebp
00072983:  push   %edi
00072984:  push   %esi
00072985:  push   %ebx
00072986:  call   624a9               ; __x86.get_pc_thunk (PIC setup)
0007298b:  add    $0x1fe675,%ebx
00072991:  sub    $0x9c,%esp          ; large stack frame
00072997:  mov    0xc(%ebp),%edi      ; edi = text (wchar_t*)
000729a5:  mov    0x8(%ebp),%esi      ; esi = this
000729a8:  test   %edi,%edi            ; if (!text) return
000729b0:  mov    0x290(%esi),%eax    ; load current font (HFont)
000729b6:  test   %eax,%eax           ; if (!font) return
000729be:  mov    0x8(%esi),%edx      ; base x
000729c4:  add    0x2c(%esi),%edx     ; + text offset x
000729db:  push   %eax                ; font
000729dc:  push   %esi                ; this
000729dd:  call   *0x114(%edx)        ; vtable[0x114/4=69] → GetFontTall or similar
```

**Evidence**: Large function (~800+ bytes) that:
1. Reads the current font from `this+0x290` (matching DrawSetTextFont's store)
2. Computes render position from base coordinates + text offset
3. Calls vtable[0x114] (a font metrics function)
4. Contains complex text layout/rendering logic with stack frame of 0x9c bytes
5. References the `FinishDrawing` string indirectly through the rendering pipeline

---

### 5. CreateFont() → **Slot 66**
- **File offset**: `0x0006a2d0`
- **Runtime**: `0xe2e582d0` (load_bias + 0x6a2d0)

```asm
0006a2d0:  push   %ebp
0006a2d1:  mov    %esp,%ebp
0006a2d3:  push   %ebx
0006a2d4:  call   624a9               ; __x86.get_pc_thunk
0006a2d9:  add    $0x206d27,%ebx
0006a2df:  sub    $0x4,%esp
0006a2e2:  call   1693b0              ; → get CFontManager* (PIC global lookup)
0006a2e7:  sub    $0xc,%esp
0006a2ea:  push   %eax                ; pass font manager
0006a2eb:  call   16a200              ; → CFontManager::CreateFont() → returns HFont
0006a2f0:  mov    -0x4(%ebp),%ebx
0006a2f3:  leave
0006a2f4:  ret                       ; return HFont in eax
```

**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`.

---

### 6. AddGlyphSetToFont(HFont font, const char* windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin, int nRangeMax) → **Slot 67**
- **File offset**: `0x0006a300`
- **Runtime**: `0xe2e58300` (load_bias + 0x6a300)

```asm
0006a300:  push   %ebp
0006a301:  mov    %esp,%ebp
0006a303:  push   %ebx
0006a304:  call   624a9               ; __x86.get_pc_thunk
0006a309:  add    $0x206cf7,%ebx
0006a30f:  sub    $0x4,%esp
0006a312:  call   1693b0              ; → get CFontManager*
0006a317:  sub    $8,%esp
0006a31a:  push   0x2c(%ebp)          ; arg 9: nRangeMax
0006a31d:  push   0x28(%ebp)          ; arg 8: nRangeMin
0006a320:  push   0x24(%ebp)          ; arg 7: flags
0006a323:  push   0x20(%ebp)          ; arg 6: scanlines
0006a326:  push   0x1c(%ebp)          ; arg 5: blur
0006a329:  push   0x18(%ebp)          ; arg 4: weight
0006a32c:  push   0x14(%ebp)          ; arg 3: tall
0006a32f:  push   0x10(%ebp)          ; arg 2: windowsFontName (const char*)
0006a332:  push   0x0c(%ebp)          ; arg 1: font (HFont)
0006a335:  push   %eax                ; font manager
0006a336:  call   16a550              ; → CFontManager::SetFontGlyphSet()
0006a33b:  mov    -0x4(%ebp),%ebx
0006a33e:  leave
0006a33f:  ret
```

**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:
- Multiplies HFont by `0x3c` (60 bytes per font entry) to index into the font array
- Calls `0x169ec0` to resolve the font name string
- Calls `0x16a220` to configure the font with FreeType parameters

Note: 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.

---

## Summary Table

| Slot | Function | File Offset | Runtime Address | Member Offset |
|------|----------|-------------|-----------------|---------------|
| 17 | `DrawSetTextFont(HFont)` | `0x00069360` | `0xe2e57360` | `this+0x290` |
| 18 | `DrawSetTextColor(int,int,int,int)` | `0x00069380` | `0xe2e57380` | `this+0x28..0x2b` |
| 20 | `DrawSetTextPos(int,int)` | `0x00069440` | `0xe2e57440` | `this+0x2c, +0x30` |
| 22 | `DrawPrintText(wchar_t*,int,FontDrawType_t)` | `0x00072980` | `0xe2e60980` | reads `this+0x290` |
| 66 | `CreateFont()` | `0x0006a2d0` | `0xe2e582d0` | returns HFont |
| 67 | `AddGlyphSetToFont(HFont, const char*, int×7)` | `0x0006a300` | `0xe2e58300` | calls `0x16a550` |

## Related Slots

| Slot | Function | Evidence |
|------|----------|----------|
| 10 | `DrawSetColor(int,int,int,int)` | stores at `this+0x24..0x27` (shape color) |
| 11 | `DrawSetColor(Color_t)` | extracts packed int, stores at `this+0x24..0x27` |
| 19 | `DrawSetTextColor(Color_t)` | stores at `this+0x28..0x2b` (text color, via packed Color_t) |
| 21 | `DrawGetTextPos(int&,int&)` | reads from `this+0x2c, +0x30` (inverse of DrawSetTextPos) |

## CMatSystemSurface Member Layout (inferred)

| Offset | Type | Field |
|--------|------|-------|
| `+0x00` | `void**` | vtable pointer |
| `+0x10` | `float` | `m_flAlphaMultiplier` |
| `+0x24` | `uint8[4]` | `drawColor` (RGBA for shapes) |
| `+0x28` | `uint8[4]` | `textColor` (RGBA for text) |
| `+0x2c` | `int` | `m_nTextX` |
| `+0x30` | `int` | `m_nTextY` |
| `+0x290` | `HFont` | `m_hCurrentFont` |