File size: 9,261 Bytes
8f33a03
1f2d77a
8f33a03
 
 
 
 
1f2d77a
8f33a03
1f2d77a
 
8f33a03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f2d77a
 
8f33a03
 
 
 
 
 
 
 
1f2d77a
 
8f33a03
 
1f2d77a
 
 
8f33a03
1f2d77a
8f33a03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f2d77a
8f33a03
 
 
 
 
 
 
 
 
 
1f2d77a
8f33a03
 
 
 
 
 
 
1f2d77a
8f33a03
 
 
 
 
 
 
 
 
 
 
 
 
 
1f2d77a
8f33a03
1f2d77a
8f33a03
 
 
 
 
 
 
1f2d77a
 
 
 
 
8f33a03
 
 
 
 
 
 
 
 
1f2d77a
8f33a03
1f2d77a
 
 
 
8f33a03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# Frontend Navigation State Machine

## Overview

The WA frontend uses MFC `CDialog` modal dialogs driven by a main navigation loop.
The loop is at `0x4E6440` (`Frontend__MainNavigationLoop`), which is the `CWinApp::Run()`
virtual method override β€” a 19KB function that handles initialization and screen dispatch.

## Architecture

```
g_CurrentScreen (0x6B3504)
        β”‚
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Main Navigation Loop β”‚  (0x4E6440)
β”‚ do {                 β”‚
β”‚   switch(screenId) { β”‚
β”‚     case 10: ...     │──► Create dialog ──► DoModal() ──► result
β”‚     case 11: ...     β”‚                                       β”‚
β”‚     ...              β”‚                                       β”‚
β”‚   }                  │◄──── store result in g_CurrentScreen β”€β”˜
β”‚ } while(true);       β”‚
β”‚                      β”‚
β”‚ case 15: EXIT        │──► cleanup, return
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

Each iteration:
1. Reads `g_CurrentScreen` (0x6B3504)
2. Dispatches to the matching case β†’ allocates dialog β†’ calls constructor β†’ DoModal
3. `FrontendChangeScreen(screen_id)` inside the dialog calls `EndDialog(screen_id)`
4. DoModal returns the result code β†’ stored back into `g_CurrentScreen`
5. Previous dialog destroyed via `Frontend__PostScreenCleanup` (0x4EB450)
6. Loop continues

## FrontendChangeScreen (0x447A20)

`stdcall(screen_id)` with `ESI` = dialog this pointer (MSVC `__usercall`).

Two modes based on `g_FrontendFrame` (0x6B3908):

- **Normal** (g_FrontendFrame != 0): Disable window β†’ palette transition
  (0x422180) β†’ call vtable[0x15C] twice (args 1, 2) β†’ re-enable window β†’
  CDialog::EndDialog(screen_id)
- **Init** (g_FrontendFrame == 0): Store screen_id at dialog+0x44, clear flag
  bit 0x10 at dialog+0x3C

## Screen Dispatch Table

### Valid main-loop screens (10–30)

| ID | Hex | Name | Constructor | Notes |
|----|-----|------|-------------|-------|
| 10 | 0x0A | Deathmatch | 0x440F40 | `PreTransitionCleanup` before |
| 11 | 0x0B | LocalMultiplayer | 0x49C420 | `PreTransitionCleanup` before |
| 12 | 0x0C | Training | 0x4E0880 | Palette load before |
| 14 | 0x0E | Missions | 0x499190 | Palette load before |
| 15 | 0x0F | **Exit** | β€” | Cleanup + return from CWinApp::Run |
| 16 | 0x10 | PostInitMenu | 0x4C91B0 | Calls 0x4E4BD0 after |
| 17 | 0x11 | MainMenuRedirect | β€” | Sets g_CurrentScreen = 18 |
| 18 | 0x12 | **MainMenu** | 0x4866C0 | Primary menu dialog |
| 21 | 0x15 | SinglePlayer | 0x4D69F0 | Palette load before |
| 22 | 0x16 | CampaignA | 0x4A2B70 | `PreTransitionCleanup` after |
| 23 | 0x17 | CampaignB | 0x4A24D0 | `PreTransitionCleanup` after |
| 24 | 0x18 | AdvancedSettings | 0x4279E0 | Calls 0x4E4BD0 after |
| 25 | 0x19 | IntroMovie | 0x470870 | Hardcodes next=18, sets g_SkipToMainMenu |
| 27 | 0x1B | NetworkHostSetup | 0x4ADCA0 | `PreTransitionCleanup` after |
| 28 | 0x1C | NetworkOnlineSetup | 0x4ACBC0 | `PreTransitionCleanup` after |
| 29 | 0x1D | NetworkDialogA | 0x4BC970 | `PreTransitionCleanup` after |
| 30 | 0x1E | NetworkDialogB | 0x4BBC00 | `PreTransitionCleanup` after |

### Network/lobby screens (1700–1707)

| ID | Hex | Name | Constructor | Notes |
|----|-----|------|-------------|-------|
| 1700 | 0x6A4 | NetworkProviderSelect | 0x4A7990 | Special post-DoModal logic |
| 1701 | 0x6A5 | NetworkSettings | 0x4C23C0 | |
| 1702 | 0x6A6 | LAN | 0x480A80 | |
| 1703 | 0x6A7 | WormNet | 0x472400 | |
| 1704 | 0x6A8 | LobbyHost | 0x4B0160 | Fall-through from setup |
| 1705 | 0x6A9 | LobbyHostContinued | (fall-through) | |
| 1706 | 0x6AA | LobbyClient | (fall-through) | |
| 1707 | 0x6AB | LobbyGameStart | 0x4BDBE0 | |

### Dialog result codes (NOT dispatched by main loop)

These are returned by `EndDialog` from sub-dialogs (button handlers, options screens).
If they reach the main loop, they hit the default case β†’ `g_CurrentScreen = 0x0F` β†’ exit.
By that point, the game has already launched or the action has been taken.

| ID | Hex | Name | Meaning |
|----|-----|------|---------|
| 0 | 0x00 | Cancel | Return to previous / back |
| 1 | 0x01 | Splash | Splash/intro trigger |
| 2 | 0x02 | NetworkOptionA | Network option result |
| 4 | 0x04 | SinglePlayerPrep | SP prep result |
| 5 | 0x05 | SinglePlayerConfirm | SP confirm result |
| 32 | 0x20 | SchemeEditorDone | Return from scheme/weapon editor |
| 35 | 0x23 | StartGameOffline | Start game, no network |
| 36 | 0x24 | StartGameOnline | Start game, network available |
| 37 | 0x25 | MultiplayerLocal | Multiplayer, no network |
| 38 | 0x26 | MultiplayerOnline | Multiplayer, network available |
| 39 | 0x27 | OptionsAccepted | Return after applying options + saving theme |
| 50 | 0x32 | InGameA | In-game state |
| 52 | 0x34 | InGameB | In-game state |
| 58 | 0x3A | InGameC | In-game state |

#### Offline/Online pairs

The "Start Game" and "Multiplayer" paths each check a network availability flag
and branch to an offline/online result code:

- **Start Game** (`this+0x244` flag): 35 (offline) / 36 (online)
- **Multiplayer** (`this+0x140` flag): 37 (offline) / 38 (online)

Both paths use 0 (Cancel) as the "back to main menu" value.

## Key Functions

| Address | Name | Purpose |
|---------|------|---------|
| 0x4E6440 | Frontend__MainNavigationLoop | CWinApp::Run override β€” main dispatch loop |
| 0x447A20 | FrontendChangeScreen | Ends current dialog via EndDialog(screen_id) |
| 0x447960 | Frontend__DoModalWrapper | Palette transition + custom DoModal |
| 0x40FD60 | CDialog__DoModal_Custom | Custom MFC DoModal |
| 0x40FBE0 | CDialog__CustomMsgPump | Custom message pump (replaces RunModalLoop) |
| 0x40FF90 | FrontendDialog__OnIdle | Per-frame idle: cursor, mouse, paint dispatch |
| 0x40BF60 | FrontendDialog__PaintControlTree | Traverse control tree, paint dirty controls |
| 0x4EB450 | Frontend__PostScreenCleanup | Destroys previous dialog |
| 0x4E4AE0 | Frontend__PreTransitionCleanup | Cleanup before certain transitions |
| 0x422180 | Frontend__PaletteAnimation | Palette transition effect |
| 0x446BA0 | FrontendDialog__Constructor | Base frontend dialog constructor |
| 0x44E850 | Frontend__OnMultiplayerButton | Multiplayer button handler |
| 0x44EC10 | Frontend__OnNetworkButton | Network button handler |
| 0x48DAB0 | Frontend__OnOptionsAccept | Options accept handler |
| 0x4F14A0 | Frontend__OnStartGameButton | Start game button handler |
| 0x4D4920 | Network__IsAvailable | Check network availability |

## Key Globals

| Address | Name | Type | Description |
|---------|------|------|-------------|
| 0x6B3504 | g_CurrentScreen | u32 | Screen ID driving the main dispatch loop |
| 0x6B3908 | g_FrontendFrame | CFrameWnd* | Main frame window |
| 0x6B390C | g_FrontendHwnd | HWND | Main window handle |
| 0x79D6D4 | g_DDDisplayWrapper | void* | Display backend wrapper (valid during entire frontend) |
| 0x7A083D | g_SkipToMainMenu | u8 | Skip splash, go directly to main menu |
| 0x7A083F | g_AutoNetworkFlag | u8 | Auto-network mode flag |
| 0x7C0A20 | g_MainMenuActive | u8 | 0xFF during screen 18 |
| 0x7C03D0 | g_CWinApp | CWinApp* | MFC application singleton |
| 0x7C0D40 | g_NetworkMode | u32 | 0=LAN, nonzero=WormNET |
| 0x7C0D68 | g_NetworkSubtype | u32 | Network sub-type selector |

## Bitmap Font System

WA uses a custom bitmap font renderer, NOT GDI, for all game/frontend text.

### Font files
- `Graphics\Font.bmp` + `Data\Gfx\FontExt\%s2.fex` (extended glyphs)
- Four sizes: `SmlFont2.bmp` (12px), `StdFont2.bmp` (17px), `MedFont2.bmp` (25px), `BigFont2.bmp` (33px)

### Key structures
- Font array at `0x7A0F58`, each font is `0x241C` bytes
- Glyph table: 256 entries at `font+0x14`, each `0x24` bytes
- Character width table at `0x6B2DD9` (256 bytes)
- Registry options: `LargerFonts` (font+0x20), `FrontendFontDynamicAntialiasing` (font+0x70)

### Key functions

| Address | Name | Description |
|---------|------|-------------|
| 0x414680 | Font__LoadFonts | Loads all four font sizes from BMP files |
| 0x4143D0 | Font__RenderGlyphs | Core glyph renderer: iterates chars, blits from sprite sheet |
| 0x427830 | Font__DrawText | Higher-level: measures string, creates surface, renders |
| 0x5236B0 | DDDisplay__DrawTextOnBitmap | thiscall(font_id, bitmap, hAlign, vAlign, msg, a7, a8) |
| 0x4FAF00 | DDDisplay__ConstructTextbox | thiscall(dst, length, fontid) |
| 0x4FB070 | SetTextboxText | stdcall(textbox, msg, textcolor, color1, color2, a6, a7, opacity) |
| 0x542200 | DrawTextboxLocal | Draws textbox at screen position |

### Rendering pipeline
1. Create surface via `g_DDDisplayWrapper->vtable[22]` (CreateSurface factory)
2. `Font__RenderGlyphs` iterates characters, looks up glyph data in font table
3. Each glyph is blitted from the font sprite sheet onto the surface
4. Surface blitted to screen via `Surface__Blit` (0x403BF0 β†’ surface->vtable[11])

### Display backends
The display wrapper at `0x79D6D4` (vtable `0x662EC8`, size 0x1C) delegates to one of 6 backends:
DirectDraw legacy, DirectDraw classic, D3D9 basic, D3D9 shader, OpenGL CPU, OpenGL GPU.
Concrete backend stored at `wrapper+0x18`. Initialized in the main navigation loop
before any dialog is created.