paavohtl Claude Opus 4.6 commited on
Commit
8f33a03
Β·
1 Parent(s): 903360a

docs: frontend state machine RE + 47 Ghidra labels

Browse files

Document the complete frontend navigation state machine:
- Main navigation loop at 0x4E6440 (CWinApp::Run, 19KB)
- g_CurrentScreen (0x6B3504) drives screen dispatch
- 20 dialog constructors mapped to screen IDs
- Bitmap font system: 4 font sizes, glyph renderer, textbox API
- Display wrapper at 0x79D6D4 with 6 backend variants

47 new Ghidra labels applied (functions, globals, dialog ctors).
38 new address.rs constants: dialog ctors, font system, display globals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

crates/openwa-types/src/address.rs CHANGED
@@ -187,6 +187,36 @@ pub mod va {
187
  pub const CDIALOG_DO_MODAL_CUSTOM: u32 = 0x0040_FD60;
188
  /// Custom message pump (replaces MFC's RunModalLoop)
189
  pub const CDIALOG_CUSTOM_MSG_PUMP: u32 = 0x0040_FBE0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
  // === Lobby / network ===
192
 
@@ -207,10 +237,29 @@ pub mod va {
207
  pub const WA_MALLOC_MEMSET: u32 = 0x0053_E910;
208
  pub const WA_FREE: u32 = 0x005D_0D2B;
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  // === Global variables (in .data) ===
211
 
212
  /// Current screen ID driving the main navigation loop dispatch
213
  pub const G_CURRENT_SCREEN: u32 = 0x006B_3504;
 
 
214
  /// Main frontend frame window (CWnd*)
215
  pub const G_FRONTEND_FRAME: u32 = 0x006B_3908;
216
  /// Main frontend HWND
@@ -219,8 +268,14 @@ pub mod va {
219
  pub const G_SKIP_TO_MAIN_MENU: u32 = 0x007A_083D;
220
  /// Auto-network mode flag
221
  pub const G_AUTO_NETWORK_FLAG: u32 = 0x007A_083F;
 
 
 
 
222
  /// Main menu active flag (0xFF during screen 18)
223
  pub const G_MAIN_MENU_ACTIVE: u32 = 0x007C_0A20;
 
 
224
  /// Network mode (0=LAN, nonzero=WormNET)
225
  pub const G_NETWORK_MODE: u32 = 0x007C_0D40;
226
  /// Network sub-type selector
 
187
  pub const CDIALOG_DO_MODAL_CUSTOM: u32 = 0x0040_FD60;
188
  /// Custom message pump (replaces MFC's RunModalLoop)
189
  pub const CDIALOG_CUSTOM_MSG_PUMP: u32 = 0x0040_FBE0;
190
+ /// Per-frame idle handler: cursor, mouse, paint dispatch
191
+ pub const FRONTEND_DIALOG_ON_IDLE: u32 = 0x0040_FF90;
192
+ /// Traverses control tree, paints dirty controls
193
+ pub const FRONTEND_DIALOG_PAINT_CONTROL_TREE: u32 = 0x0040_BF60;
194
+ /// Renders background image for frontend dialog
195
+ pub const FRONTEND_DIALOG_RENDER_BACKGROUND: u32 = 0x0040_4250;
196
+ /// Surface blit operation (calls surface->vtable[11])
197
+ pub const SURFACE_BLIT: u32 = 0x0040_3BF0;
198
+
199
+ // === Frontend dialog constructors (from main navigation loop) ===
200
+
201
+ pub const FRONTEND_DEATHMATCH_CTOR: u32 = 0x0044_0F40;
202
+ pub const FRONTEND_LOCAL_MP_CTOR: u32 = 0x0049_C420;
203
+ pub const FRONTEND_TRAINING_CTOR: u32 = 0x004E_0880;
204
+ pub const FRONTEND_MISSIONS_CTOR: u32 = 0x0049_9190;
205
+ pub const FRONTEND_POST_INIT_CTOR: u32 = 0x004C_91B0;
206
+ pub const FRONTEND_MAIN_MENU_CTOR: u32 = 0x0048_66C0;
207
+ pub const FRONTEND_SINGLE_PLAYER_CTOR: u32 = 0x004D_69F0;
208
+ pub const FRONTEND_CAMPAIGN_A_CTOR: u32 = 0x004A_2B70;
209
+ pub const FRONTEND_CAMPAIGN_B_CTOR: u32 = 0x004A_24D0;
210
+ pub const FRONTEND_ADV_SETTINGS_CTOR: u32 = 0x0042_79E0;
211
+ pub const FRONTEND_INTRO_MOVIE_CTOR: u32 = 0x0047_0870;
212
+ pub const FRONTEND_NETWORK_HOST_CTOR: u32 = 0x004A_DCA0;
213
+ pub const FRONTEND_NETWORK_ONLINE_CTOR: u32 = 0x004A_CBC0;
214
+ pub const FRONTEND_NETWORK_PROVIDER_CTOR: u32 = 0x004A_7990;
215
+ pub const FRONTEND_NETWORK_SETTINGS_CTOR: u32 = 0x004C_23C0;
216
+ pub const FRONTEND_LAN_CTOR: u32 = 0x0048_0A80;
217
+ pub const FRONTEND_WORMNET_CTOR: u32 = 0x0047_2400;
218
+ pub const FRONTEND_LOBBY_HOST_CTOR: u32 = 0x004B_0160;
219
+ pub const FRONTEND_LOBBY_GAME_START_CTOR: u32 = 0x004B_DBE0;
220
 
221
  // === Lobby / network ===
222
 
 
237
  pub const WA_MALLOC_MEMSET: u32 = 0x0053_E910;
238
  pub const WA_FREE: u32 = 0x005D_0D2B;
239
 
240
+ // === Bitmap font system ===
241
+
242
+ /// Loads all four font sizes from BMP files
243
+ pub const FONT_LOAD_FONTS: u32 = 0x0041_4680;
244
+ /// Core glyph renderer: iterates chars, blits from sprite sheet
245
+ pub const FONT_RENDER_GLYPHS: u32 = 0x0041_43D0;
246
+ /// Higher-level text draw: measures string, creates surface, renders
247
+ pub const FONT_DRAW_TEXT: u32 = 0x0042_7830;
248
+ /// DDDisplay::DrawTextOnBitmap β€” thiscall(font_id, bitmap, hAlign, vAlign, msg, a7, a8)
249
+ pub const DDDISPLAY_DRAW_TEXT_ON_BITMAP: u32 = 0x0052_36B0;
250
+ /// DDDisplay::ConstructTextbox β€” thiscall(dst, length, fontid)
251
+ pub const DDDISPLAY_CONSTRUCT_TEXTBOX: u32 = 0x004F_AF00;
252
+ /// SetTextboxText β€” stdcall(textbox, msg, textcolor, color1, color2, a6, a7, opacity)
253
+ pub const SET_TEXTBOX_TEXT: u32 = 0x004F_B070;
254
+ /// DrawTextboxLocal β€” draws textbox at screen position
255
+ pub const DRAW_TEXTBOX_LOCAL: u32 = 0x0054_2200;
256
+
257
  // === Global variables (in .data) ===
258
 
259
  /// Current screen ID driving the main navigation loop dispatch
260
  pub const G_CURRENT_SCREEN: u32 = 0x006B_3504;
261
+ /// Character width table (256 bytes)
262
+ pub const G_CHAR_WIDTH_TABLE: u32 = 0x006B_2DD9;
263
  /// Main frontend frame window (CWnd*)
264
  pub const G_FRONTEND_FRAME: u32 = 0x006B_3908;
265
  /// Main frontend HWND
 
268
  pub const G_SKIP_TO_MAIN_MENU: u32 = 0x007A_083D;
269
  /// Auto-network mode flag
270
  pub const G_AUTO_NETWORK_FLAG: u32 = 0x007A_083F;
271
+ /// DDDisplayWrapper pointer (valid during entire frontend lifetime)
272
+ pub const G_DD_DISPLAY_WRAPPER: u32 = 0x0079_D6D4;
273
+ /// Font array β€” 4 fonts, each 0x241C bytes
274
+ pub const G_FONT_ARRAY: u32 = 0x007A_0F58;
275
  /// Main menu active flag (0xFF during screen 18)
276
  pub const G_MAIN_MENU_ACTIVE: u32 = 0x007C_0A20;
277
+ /// MFC CWinApp singleton
278
+ pub const G_CWINAPP: u32 = 0x007C_03D0;
279
  /// Network mode (0=LAN, nonzero=WormNET)
280
  pub const G_NETWORK_MODE: u32 = 0x007C_0D40;
281
  /// Network sub-type selector
docs/re-notes/frontend-screens.md CHANGED
@@ -1,86 +1,191 @@
1
- # Frontend Screen System
2
 
3
- ## Architecture
 
 
 
 
4
 
5
- The frontend uses **MFC CDialog modal dialogs**, not the CTask class hierarchy.
6
- Screen transitions work through a centralized dispatcher:
7
 
8
  ```
9
- FrontendFrame (global at 0x6B3908, constructed at 0x4ECCA0)
10
- └── CDialog modal loop (CDialog__DoModal_Custom at 0x40FD60)
11
- β”œβ”€β”€ Button handler calls FrontendChangeScreen(screen_id)
12
- β”œβ”€β”€ FrontendChangeScreen calls CDialog::EndDialog(screen_id)
13
- β”œβ”€β”€ DoModal returns screen_id to caller
14
- └── Caller decides next dialog based on return value
 
 
 
 
 
 
 
 
 
15
  ```
16
 
 
 
 
 
 
 
 
 
17
  ## FrontendChangeScreen (0x447A20)
18
 
 
 
19
  Two modes based on `g_FrontendFrame` (0x6B3908):
20
 
21
  - **Normal** (g_FrontendFrame != 0): Disable window β†’ palette transition
22
- (0x422180) β†’ call vtable method at +0x15C twice β†’ re-enable window β†’
23
  CDialog::EndDialog(screen_id)
24
- - **Init** (g_FrontendFrame == 0): Store screen_id at ESI+0x44, clear flag bit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- ## Screen IDs
 
 
 
 
 
 
 
 
 
27
 
28
- | ID | Hex | Name | Context |
 
 
 
 
 
 
29
  |----|-----|------|---------|
30
- | 0 | 0x00 | Cancel | Return to previous / close dialog |
31
- | 10 | 0x0A | MainMenu | Main menu screen |
32
- | 11 | 0x0B | LocalMultiplayer | Local multiplayer setup |
33
- | 15 | 0x0F | Minimized | Window minimized (IsIconic check) |
34
- | 16 | 0x10 | InitialLoad | Initial game loading screen |
35
- | 21 | 0x15 | SinglePlayer | Single player / Deathmatch |
36
- | 26 | 0x1A | NetworkSubScreen | Network options sub-screen |
37
- | 35 | 0x23 | StartGameA | Start game (path A) |
38
- | 36 | 0x24 | StartGameB | Start game (path B) |
39
- | 37 | 0x25 | NetworkSetupA | Network setup (path A) |
40
- | 38 | 0x26 | NetworkSetupB | Network setup (path B) |
41
- | 39 | 0x27 | OptionsAccept | Options saved (writes current.thm) |
42
- | 1700 | 0x6A4 | NetworkSelection | Network provider selection |
43
- | 1702 | 0x6A6 | Lan | LAN game |
44
- | 1703 | 0x6A7 | WormNet | WormNET online lobby |
45
- | 1704 | 0x6A8 | LobbyHost | Hosting a game |
46
- | 1706 | 0x6AA | LobbyClient | Joining a game |
47
- | 1707 | 0x6AB | LobbyGameStart | Lobby β†’ game start |
48
-
49
- Note: Some callers pass dynamic screen IDs. There may be additional IDs not
50
- discovered through static analysis.
51
 
52
- ## Key Globals
53
 
54
- | Address | Name | Type | Description |
55
- |---------|------|------|-------------|
56
- | 0x6B3908 | g_FrontendFrame | CWnd* | Main application frame window |
57
- | 0x6B390C | g_FrontendHwnd | HWND | Main window handle |
58
- | 0x7C0D40 | g_NetworkMode | int | 0=LAN, nonzero=WormNET |
59
- | 0x7C0D68 | g_NetworkSubtype | int | Network sub-type selector |
 
60
 
61
  ## Key Functions
62
 
63
  | Address | Name | Purpose |
64
  |---------|------|---------|
65
- | 0x40FD60 | CDialog__DoModal_Custom | Custom DoModal returning screen ID |
 
 
 
 
 
 
 
 
66
  | 0x422180 | Frontend__PaletteAnimation | Palette transition effect |
67
- | 0x429830 | Frontend__OnInitialLoad | Initial game load handler |
68
- | 0x441D80 | Frontend__LaunchSinglePlayer | Single player launch |
69
- | 0x446BA0 | FrontendDialog__Constructor | Dialog constructor |
70
- | 0x447A20 | FrontendChangeScreen | Central screen transition dispatcher |
71
- | 0x447AA0 | Frontend__LoadTransitionPalette | Load transition palette |
72
  | 0x44E850 | Frontend__OnMultiplayerButton | Multiplayer button handler |
73
  | 0x44EC10 | Frontend__OnNetworkButton | Network button handler |
74
- | 0x486A10 | Frontend__OnMinimize | Window minimize handler |
75
  | 0x48DAB0 | Frontend__OnOptionsAccept | Options accept handler |
76
- | 0x493CB0 | Lobby__DisplayMessage | Display lobby chat message |
77
- | 0x4AA990 | Lobby__SendGreentext | Send colored lobby text |
78
- | 0x4B7E20 | Lobby__PrintUsedVersion | Print version to lobby |
79
- | 0x4BAE40 | Lobby__OnDisconnect | Handle lobby disconnect |
80
- | 0x4BAEC0 | Lobby__OnGameEnd | Handle game end in lobby |
81
- | 0x4BD400 | Lobby__OnMessage | Handle lobby message |
82
- | 0x4CD9A0 | LobbyDialog__Constructor | Lobby dialog constructor |
83
- | 0x4D4920 | Network__IsAvailable | Check network availability |
84
- | 0x4E4AE0 | Frontend__PreTransitionCleanup | Pre-transition cleanup |
85
- | 0x4ECCA0 | FrontendFrame__Constructor | Frame window constructor |
86
  | 0x4F14A0 | Frontend__OnStartGameButton | Start game button handler |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Frontend Navigation State Machine
2
 
3
+ ## Overview
4
+
5
+ The WA frontend uses MFC `CDialog` modal dialogs driven by a main navigation loop.
6
+ The loop is at `0x4E6440` (`Frontend__MainNavigationLoop`), which is the `CWinApp::Run()`
7
+ virtual method override β€” a 19KB function that handles initialization and screen dispatch.
8
 
9
+ ## Architecture
 
10
 
11
  ```
12
+ g_CurrentScreen (0x6B3504)
13
+ β”‚
14
+ β–Ό
15
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
16
+ β”‚ Main Navigation Loop β”‚ (0x4E6440)
17
+ β”‚ do { β”‚
18
+ β”‚ switch(screenId) { β”‚
19
+ β”‚ case 10: ... │──► Create dialog ──► DoModal() ──► result
20
+ β”‚ case 11: ... β”‚ β”‚
21
+ β”‚ ... β”‚ β”‚
22
+ β”‚ } │◄──── store result in g_CurrentScreen β”€β”˜
23
+ β”‚ } while(true); β”‚
24
+ β”‚ β”‚
25
+ β”‚ case 15: EXIT │──► cleanup, return
26
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
27
  ```
28
 
29
+ Each iteration:
30
+ 1. Reads `g_CurrentScreen` (0x6B3504)
31
+ 2. Dispatches to the matching case β†’ allocates dialog β†’ calls constructor β†’ DoModal
32
+ 3. `FrontendChangeScreen(screen_id)` inside the dialog calls `EndDialog(screen_id)`
33
+ 4. DoModal returns the result code β†’ stored back into `g_CurrentScreen`
34
+ 5. Previous dialog destroyed via `Frontend__PostScreenCleanup` (0x4EB450)
35
+ 6. Loop continues
36
+
37
  ## FrontendChangeScreen (0x447A20)
38
 
39
+ `stdcall(screen_id)` with `ESI` = dialog this pointer (MSVC `__usercall`).
40
+
41
  Two modes based on `g_FrontendFrame` (0x6B3908):
42
 
43
  - **Normal** (g_FrontendFrame != 0): Disable window β†’ palette transition
44
+ (0x422180) β†’ call vtable[0x15C] twice (args 1, 2) β†’ re-enable window β†’
45
  CDialog::EndDialog(screen_id)
46
+ - **Init** (g_FrontendFrame == 0): Store screen_id at dialog+0x44, clear flag
47
+ bit 0x10 at dialog+0x3C
48
+
49
+ ## Screen Dispatch Table
50
+
51
+ ### Valid main-loop screens (10–30)
52
+
53
+ | ID | Hex | Name | Constructor | Notes |
54
+ |----|-----|------|-------------|-------|
55
+ | 10 | 0x0A | Deathmatch | 0x440F40 | `PreTransitionCleanup` before |
56
+ | 11 | 0x0B | LocalMultiplayer | 0x49C420 | `PreTransitionCleanup` before |
57
+ | 12 | 0x0C | Training | 0x4E0880 | Palette load before |
58
+ | 14 | 0x0E | Missions | 0x499190 | Palette load before |
59
+ | 15 | 0x0F | **Exit** | β€” | Cleanup + return from CWinApp::Run |
60
+ | 16 | 0x10 | PostInitMenu | 0x4C91B0 | Calls 0x4E4BD0 after |
61
+ | 17 | 0x11 | MainMenuRedirect | β€” | Sets g_CurrentScreen = 18 |
62
+ | 18 | 0x12 | **MainMenu** | 0x4866C0 | Primary menu dialog |
63
+ | 21 | 0x15 | SinglePlayer | 0x4D69F0 | Palette load before |
64
+ | 22 | 0x16 | CampaignA | 0x4A2B70 | `PreTransitionCleanup` after |
65
+ | 23 | 0x17 | CampaignB | 0x4A24D0 | `PreTransitionCleanup` after |
66
+ | 24 | 0x18 | AdvancedSettings | 0x4279E0 | Calls 0x4E4BD0 after |
67
+ | 25 | 0x19 | IntroMovie | 0x470870 | Hardcodes next=18, sets g_SkipToMainMenu |
68
+ | 27 | 0x1B | NetworkHostSetup | 0x4ADCA0 | `PreTransitionCleanup` after |
69
+ | 28 | 0x1C | NetworkOnlineSetup | 0x4ACBC0 | `PreTransitionCleanup` after |
70
+ | 29 | 0x1D | NetworkDialogA | 0x4BC970 | `PreTransitionCleanup` after |
71
+ | 30 | 0x1E | NetworkDialogB | 0x4BBC00 | `PreTransitionCleanup` after |
72
+
73
+ ### Network/lobby screens (1700–1707)
74
 
75
+ | ID | Hex | Name | Constructor | Notes |
76
+ |----|-----|------|-------------|-------|
77
+ | 1700 | 0x6A4 | NetworkProviderSelect | 0x4A7990 | Special post-DoModal logic |
78
+ | 1701 | 0x6A5 | NetworkSettings | 0x4C23C0 | |
79
+ | 1702 | 0x6A6 | LAN | 0x480A80 | |
80
+ | 1703 | 0x6A7 | WormNet | 0x472400 | |
81
+ | 1704 | 0x6A8 | LobbyHost | 0x4B0160 | Fall-through from setup |
82
+ | 1705 | 0x6A9 | LobbyHostContinued | (fall-through) | |
83
+ | 1706 | 0x6AA | LobbyClient | (fall-through) | |
84
+ | 1707 | 0x6AB | LobbyGameStart | 0x4BDBE0 | |
85
 
86
+ ### Dialog result codes (NOT dispatched by main loop)
87
+
88
+ These are returned by `EndDialog` from sub-dialogs (button handlers, options screens).
89
+ If they reach the main loop, they hit the default case β†’ `g_CurrentScreen = 0x0F` β†’ exit.
90
+ By that point, the game has already launched or the action has been taken.
91
+
92
+ | ID | Hex | Name | Meaning |
93
  |----|-----|------|---------|
94
+ | 0 | 0x00 | Cancel | Return to previous / back |
95
+ | 1 | 0x01 | Splash | Splash/intro trigger |
96
+ | 2 | 0x02 | NetworkOptionA | Network option result |
97
+ | 4 | 0x04 | SinglePlayerPrep | SP prep result |
98
+ | 5 | 0x05 | SinglePlayerConfirm | SP confirm result |
99
+ | 32 | 0x20 | SchemeEditorDone | Return from scheme/weapon editor |
100
+ | 35 | 0x23 | StartGameOffline | Start game, no network |
101
+ | 36 | 0x24 | StartGameOnline | Start game, network available |
102
+ | 37 | 0x25 | MultiplayerLocal | Multiplayer, no network |
103
+ | 38 | 0x26 | MultiplayerOnline | Multiplayer, network available |
104
+ | 39 | 0x27 | OptionsAccepted | Return after applying options + saving theme |
105
+ | 50 | 0x32 | InGameA | In-game state |
106
+ | 52 | 0x34 | InGameB | In-game state |
107
+ | 58 | 0x3A | InGameC | In-game state |
 
 
 
 
 
 
 
108
 
109
+ #### Offline/Online pairs
110
 
111
+ The "Start Game" and "Multiplayer" paths each check a network availability flag
112
+ and branch to an offline/online result code:
113
+
114
+ - **Start Game** (`this+0x244` flag): 35 (offline) / 36 (online)
115
+ - **Multiplayer** (`this+0x140` flag): 37 (offline) / 38 (online)
116
+
117
+ Both paths use 0 (Cancel) as the "back to main menu" value.
118
 
119
  ## Key Functions
120
 
121
  | Address | Name | Purpose |
122
  |---------|------|---------|
123
+ | 0x4E6440 | Frontend__MainNavigationLoop | CWinApp::Run override β€” main dispatch loop |
124
+ | 0x447A20 | FrontendChangeScreen | Ends current dialog via EndDialog(screen_id) |
125
+ | 0x447960 | Frontend__DoModalWrapper | Palette transition + custom DoModal |
126
+ | 0x40FD60 | CDialog__DoModal_Custom | Custom MFC DoModal |
127
+ | 0x40FBE0 | CDialog__CustomMsgPump | Custom message pump (replaces RunModalLoop) |
128
+ | 0x40FF90 | FrontendDialog__OnIdle | Per-frame idle: cursor, mouse, paint dispatch |
129
+ | 0x40BF60 | FrontendDialog__PaintControlTree | Traverse control tree, paint dirty controls |
130
+ | 0x4EB450 | Frontend__PostScreenCleanup | Destroys previous dialog |
131
+ | 0x4E4AE0 | Frontend__PreTransitionCleanup | Cleanup before certain transitions |
132
  | 0x422180 | Frontend__PaletteAnimation | Palette transition effect |
133
+ | 0x446BA0 | FrontendDialog__Constructor | Base frontend dialog constructor |
 
 
 
 
134
  | 0x44E850 | Frontend__OnMultiplayerButton | Multiplayer button handler |
135
  | 0x44EC10 | Frontend__OnNetworkButton | Network button handler |
 
136
  | 0x48DAB0 | Frontend__OnOptionsAccept | Options accept handler |
 
 
 
 
 
 
 
 
 
 
137
  | 0x4F14A0 | Frontend__OnStartGameButton | Start game button handler |
138
+ | 0x4D4920 | Network__IsAvailable | Check network availability |
139
+
140
+ ## Key Globals
141
+
142
+ | Address | Name | Type | Description |
143
+ |---------|------|------|-------------|
144
+ | 0x6B3504 | g_CurrentScreen | u32 | Screen ID driving the main dispatch loop |
145
+ | 0x6B3908 | g_FrontendFrame | CFrameWnd* | Main frame window |
146
+ | 0x6B390C | g_FrontendHwnd | HWND | Main window handle |
147
+ | 0x79D6D4 | g_DDDisplayWrapper | void* | Display backend wrapper (valid during entire frontend) |
148
+ | 0x7A083D | g_SkipToMainMenu | u8 | Skip splash, go directly to main menu |
149
+ | 0x7A083F | g_AutoNetworkFlag | u8 | Auto-network mode flag |
150
+ | 0x7C0A20 | g_MainMenuActive | u8 | 0xFF during screen 18 |
151
+ | 0x7C03D0 | g_CWinApp | CWinApp* | MFC application singleton |
152
+ | 0x7C0D40 | g_NetworkMode | u32 | 0=LAN, nonzero=WormNET |
153
+ | 0x7C0D68 | g_NetworkSubtype | u32 | Network sub-type selector |
154
+
155
+ ## Bitmap Font System
156
+
157
+ WA uses a custom bitmap font renderer, NOT GDI, for all game/frontend text.
158
+
159
+ ### Font files
160
+ - `Graphics\Font.bmp` + `Data\Gfx\FontExt\%s2.fex` (extended glyphs)
161
+ - Four sizes: `SmlFont2.bmp` (12px), `StdFont2.bmp` (17px), `MedFont2.bmp` (25px), `BigFont2.bmp` (33px)
162
+
163
+ ### Key structures
164
+ - Font array at `0x7A0F58`, each font is `0x241C` bytes
165
+ - Glyph table: 256 entries at `font+0x14`, each `0x24` bytes
166
+ - Character width table at `0x6B2DD9` (256 bytes)
167
+ - Registry options: `LargerFonts` (font+0x20), `FrontendFontDynamicAntialiasing` (font+0x70)
168
+
169
+ ### Key functions
170
+
171
+ | Address | Name | Description |
172
+ |---------|------|-------------|
173
+ | 0x414680 | Font__LoadFonts | Loads all four font sizes from BMP files |
174
+ | 0x4143D0 | Font__RenderGlyphs | Core glyph renderer: iterates chars, blits from sprite sheet |
175
+ | 0x427830 | Font__DrawText | Higher-level: measures string, creates surface, renders |
176
+ | 0x5236B0 | DDDisplay__DrawTextOnBitmap | thiscall(font_id, bitmap, hAlign, vAlign, msg, a7, a8) |
177
+ | 0x4FAF00 | DDDisplay__ConstructTextbox | thiscall(dst, length, fontid) |
178
+ | 0x4FB070 | SetTextboxText | stdcall(textbox, msg, textcolor, color1, color2, a6, a7, opacity) |
179
+ | 0x542200 | DrawTextboxLocal | Draws textbox at screen position |
180
+
181
+ ### Rendering pipeline
182
+ 1. Create surface via `g_DDDisplayWrapper->vtable[22]` (CreateSurface factory)
183
+ 2. `Font__RenderGlyphs` iterates characters, looks up glyph data in font table
184
+ 3. Each glyph is blitted from the font sprite sheet onto the surface
185
+ 4. Surface blitted to screen via `Surface__Blit` (0x403BF0 β†’ surface->vtable[11])
186
+
187
+ ### Display backends
188
+ The display wrapper at `0x79D6D4` (vtable `0x662EC8`, size 0x1C) delegates to one of 6 backends:
189
+ DirectDraw legacy, DirectDraw classic, D3D9 basic, D3D9 shader, OpenGL CPU, OpenGL GPU.
190
+ Concrete backend stored at `wrapper+0x18`. Initialized in the main navigation loop
191
+ before any dialog is created.