paavohtl Claude Opus 4.6 commited on
Commit
3675951
·
1 Parent(s): 5fd6579

refactor: add RQ_ prefix to render queue addresses, label pipeline in Ghidra

Browse files

Rename all RenderQueue enqueue function constants with RQ_ prefix for
consistency (e.g., DRAW_PIXEL -> RQ_DRAW_PIXEL). Add 20 new address
constants for helpers, pipeline functions, and the type 0xE enqueue.
Label 25 functions in Ghidra including 7 RQ helpers and 7 render
pipeline functions. Document complete render queue system in
docs/re-notes/render-queue.md.

Research findings:
- All 11 standalone enqueue functions identified (types 0-14 switch,
but types 3/7/10/12 have no standalone enqueue — inlined or unused)
- DDDisplay vtable drawing method map (12 slots at 0x2C-0x58)
- Render pipeline: RenderFrame -> GameRender -> RQ_RenderDrawingQueue

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

crates/openwa-harness/src/lib.rs CHANGED
@@ -409,7 +409,7 @@ mod tests {
409
  ("FireWeapon", va::FIRE_WEAPON),
410
  ("InitWeaponTable", va::INIT_WEAPON_TABLE),
411
  ("BlitScreen", va::BLIT_SCREEN),
412
- ("RenderDrawingQueue", va::RENDER_DRAWING_QUEUE),
413
  ("DrawLandscape", va::DRAW_LANDSCAPE),
414
  ("ConstructPCLandscape", va::CONSTRUCT_PC_LANDSCAPE),
415
  ("ConstructDSSound", va::CONSTRUCT_DS_SOUND),
 
409
  ("FireWeapon", va::FIRE_WEAPON),
410
  ("InitWeaponTable", va::INIT_WEAPON_TABLE),
411
  ("BlitScreen", va::BLIT_SCREEN),
412
+ ("RenderDrawingQueue", va::RQ_RENDER_DRAWING_QUEUE),
413
  ("DrawLandscape", va::DRAW_LANDSCAPE),
414
  ("ConstructPCLandscape", va::CONSTRUCT_PC_LANDSCAPE),
415
  ("ConstructDSSound", va::CONSTRUCT_DS_SOUND),
crates/openwa-types/src/address.rs CHANGED
@@ -158,16 +158,37 @@ pub mod va {
158
  pub const CONSTRUCT_DD_DISPLAY: u32 = 0x0056_9D00;
159
  pub const CONSTRUCT_FRAME_BUFFER: u32 = 0x005A_2430;
160
  pub const BLIT_SCREEN: u32 = 0x005A_2020;
161
- pub const RENDER_DRAWING_QUEUE: u32 = 0x0054_2350;
162
  pub const DRAW_LANDSCAPE: u32 = 0x005A_2790;
163
- pub const DRAW_PIXEL: u32 = 0x0054_1D60;
164
- pub const DRAW_LINE_STRIP: u32 = 0x0054_1DD0;
165
- pub const DRAW_POLYGON: u32 = 0x0054_1E50;
166
- pub const DRAW_SCALED: u32 = 0x0054_1ED0;
167
- pub const DRAW_RECT: u32 = 0x0054_1F40;
168
- pub const DRAW_SPRITE_GLOBAL: u32 = 0x0054_1FE0;
169
- pub const DRAW_SPRITE_OFFSET: u32 = 0x0054_20E0;
170
- pub const DRAW_BITMAP_GLOBAL: u32 = 0x0054_2170;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  pub const LOAD_SPRITE: u32 = 0x0052_3400;
172
  pub const CONSTRUCT_OPENGL_CPU: u32 = 0x005A_0850;
173
  pub const OPENGL_INIT: u32 = 0x0059_F000;
@@ -200,9 +221,6 @@ pub mod va {
200
  /// ProcessSprite — usercall EAX=sprite_ptr, 1 stack param (raw_data_ptr).
201
  /// Parses .spr binary format: palette, frames, bitmap data.
202
  pub const PROCESS_SPRITE: u32 = 0x004F_AB80;
203
- /// DrawSpriteLocal — thiscall + EAX=y_pos (usercall), 4 stack params.
204
- /// Enqueues draw command type 5 (screen-space). See DrawSpriteGlobal (type 4).
205
- pub const DRAW_SPRITE_LOCAL: u32 = 0x0054_2060;
206
 
207
  // === Landscape ===
208
 
@@ -388,8 +406,6 @@ pub mod va {
388
  pub const DDDISPLAY_CONSTRUCT_TEXTBOX: u32 = 0x004F_AF00;
389
  /// SetTextboxText — stdcall(textbox, msg, textcolor, color1, color2, a6, a7, opacity)
390
  pub const SET_TEXTBOX_TEXT: u32 = 0x004F_B070;
391
- /// DrawTextboxLocal — draws textbox at screen position
392
- pub const DRAW_TEXTBOX_LOCAL: u32 = 0x0054_2200;
393
 
394
  // === Global variables (in .data) ===
395
 
 
158
  pub const CONSTRUCT_DD_DISPLAY: u32 = 0x0056_9D00;
159
  pub const CONSTRUCT_FRAME_BUFFER: u32 = 0x005A_2430;
160
  pub const BLIT_SCREEN: u32 = 0x005A_2020;
161
+ pub const RQ_RENDER_DRAWING_QUEUE: u32 = 0x0054_2350;
162
  pub const DRAW_LANDSCAPE: u32 = 0x005A_2790;
163
+ pub const RQ_DRAW_PIXEL: u32 = 0x0054_1D60;
164
+ pub const RQ_DRAW_LINE_STRIP: u32 = 0x0054_1DD0;
165
+ pub const RQ_DRAW_POLYGON: u32 = 0x0054_1E50;
166
+ pub const RQ_DRAW_SCALED: u32 = 0x0054_1ED0;
167
+ pub const RQ_DRAW_RECT: u32 = 0x0054_1F40;
168
+ pub const RQ_DRAW_SPRITE_GLOBAL: u32 = 0x0054_1FE0;
169
+ pub const RQ_DRAW_SPRITE_LOCAL: u32 = 0x0054_2060;
170
+ pub const RQ_DRAW_SPRITE_OFFSET: u32 = 0x0054_20E0;
171
+ pub const RQ_DRAW_BITMAP_GLOBAL: u32 = 0x0054_2170;
172
+ pub const RQ_DRAW_TEXTBOX_LOCAL: u32 = 0x0054_2200;
173
+ pub const RQ_DRAW_CLIPPED_SPRITE_MAYBE: u32 = 0x0054_22A0;
174
+
175
+ // RenderQueue helpers
176
+ pub const RQ_CLIP_COORDINATES: u32 = 0x0054_2BA0;
177
+ pub const RQ_GET_CAMERA_OFFSET_MAYBE: u32 = 0x0054_2B10;
178
+ pub const RQ_CLIP_WITH_REF_OFFSET_MAYBE: u32 = 0x0054_2C70;
179
+ pub const RQ_TRANSFORM_WITH_ZOOM_MAYBE: u32 = 0x0054_2D50;
180
+ pub const RQ_SMOOTH_INTERPOLATE_MAYBE: u32 = 0x0054_2E60;
181
+ pub const RQ_UPDATE_CLIP_BOUNDS_MAYBE: u32 = 0x0054_2F10;
182
+ pub const RQ_SATURATE_CLIP_BOUNDS_MAYBE: u32 = 0x0054_2F70;
183
+
184
+ // Render pipeline
185
+ pub const RENDER_FRAME_MAYBE: u32 = 0x0056_E040;
186
+ pub const GAME_RENDER_MAYBE: u32 = 0x0053_3DC0;
187
+ pub const RENDER_TERRAIN_MAYBE: u32 = 0x0053_5000;
188
+ pub const RENDER_HUD_MAYBE: u32 = 0x0053_4F20;
189
+ pub const RENDER_TURN_STATUS_MAYBE: u32 = 0x0053_4E00;
190
+ pub const PALETTE_MANAGE_MAYBE: u32 = 0x0053_3C80;
191
+ pub const PALETTE_ANIMATE_MAYBE: u32 = 0x0053_3A80;
192
  pub const LOAD_SPRITE: u32 = 0x0052_3400;
193
  pub const CONSTRUCT_OPENGL_CPU: u32 = 0x005A_0850;
194
  pub const OPENGL_INIT: u32 = 0x0059_F000;
 
221
  /// ProcessSprite — usercall EAX=sprite_ptr, 1 stack param (raw_data_ptr).
222
  /// Parses .spr binary format: palette, frames, bitmap data.
223
  pub const PROCESS_SPRITE: u32 = 0x004F_AB80;
 
 
 
224
 
225
  // === Landscape ===
226
 
 
406
  pub const DDDISPLAY_CONSTRUCT_TEXTBOX: u32 = 0x004F_AF00;
407
  /// SetTextboxText — stdcall(textbox, msg, textcolor, color1, color2, a6, a7, opacity)
408
  pub const SET_TEXTBOX_TEXT: u32 = 0x004F_B070;
 
 
409
 
410
  // === Global variables (in .data) ===
411
 
crates/openwa-wormkit/src/replacements/render.rs CHANGED
@@ -605,61 +605,61 @@ pub fn install() -> Result<(), String> {
605
  unsafe {
606
  let _ = hook::install(
607
  "DrawPixel",
608
- va::DRAW_PIXEL,
609
  trampoline_draw_pixel as *const (),
610
  )?;
611
 
612
  let _ = hook::install(
613
  "DrawLineStrip",
614
- va::DRAW_LINE_STRIP,
615
  trampoline_draw_line_strip as *const (),
616
  )?;
617
 
618
  let _ = hook::install(
619
  "DrawPolygon",
620
- va::DRAW_POLYGON,
621
  trampoline_draw_polygon as *const (),
622
  )?;
623
 
624
  let _ = hook::install(
625
  "DrawScaled",
626
- va::DRAW_SCALED,
627
  trampoline_draw_scaled as *const (),
628
  )?;
629
 
630
  let _ = hook::install(
631
  "DrawRect",
632
- va::DRAW_RECT,
633
  trampoline_draw_rect as *const (),
634
  )?;
635
 
636
  let _ = hook::install(
637
  "DrawSpriteGlobal",
638
- va::DRAW_SPRITE_GLOBAL,
639
  trampoline_draw_sprite_global as *const (),
640
  )?;
641
 
642
  let _ = hook::install(
643
  "DrawSpriteLocal",
644
- va::DRAW_SPRITE_LOCAL,
645
  trampoline_draw_sprite_local as *const (),
646
  )?;
647
 
648
  let _ = hook::install(
649
  "DrawSpriteOffset",
650
- va::DRAW_SPRITE_OFFSET,
651
  trampoline_draw_sprite_offset as *const (),
652
  )?;
653
 
654
  let _ = hook::install(
655
  "DrawBitmapGlobal",
656
- va::DRAW_BITMAP_GLOBAL,
657
  trampoline_draw_bitmap_global as *const (),
658
  )?;
659
 
660
  let _ = hook::install(
661
  "DrawTextboxLocal",
662
- va::DRAW_TEXTBOX_LOCAL,
663
  trampoline_draw_textbox_local as *const (),
664
  )?;
665
 
 
605
  unsafe {
606
  let _ = hook::install(
607
  "DrawPixel",
608
+ va::RQ_DRAW_PIXEL,
609
  trampoline_draw_pixel as *const (),
610
  )?;
611
 
612
  let _ = hook::install(
613
  "DrawLineStrip",
614
+ va::RQ_DRAW_LINE_STRIP,
615
  trampoline_draw_line_strip as *const (),
616
  )?;
617
 
618
  let _ = hook::install(
619
  "DrawPolygon",
620
+ va::RQ_DRAW_POLYGON,
621
  trampoline_draw_polygon as *const (),
622
  )?;
623
 
624
  let _ = hook::install(
625
  "DrawScaled",
626
+ va::RQ_DRAW_SCALED,
627
  trampoline_draw_scaled as *const (),
628
  )?;
629
 
630
  let _ = hook::install(
631
  "DrawRect",
632
+ va::RQ_DRAW_RECT,
633
  trampoline_draw_rect as *const (),
634
  )?;
635
 
636
  let _ = hook::install(
637
  "DrawSpriteGlobal",
638
+ va::RQ_DRAW_SPRITE_GLOBAL,
639
  trampoline_draw_sprite_global as *const (),
640
  )?;
641
 
642
  let _ = hook::install(
643
  "DrawSpriteLocal",
644
+ va::RQ_DRAW_SPRITE_LOCAL,
645
  trampoline_draw_sprite_local as *const (),
646
  )?;
647
 
648
  let _ = hook::install(
649
  "DrawSpriteOffset",
650
+ va::RQ_DRAW_SPRITE_OFFSET,
651
  trampoline_draw_sprite_offset as *const (),
652
  )?;
653
 
654
  let _ = hook::install(
655
  "DrawBitmapGlobal",
656
+ va::RQ_DRAW_BITMAP_GLOBAL,
657
  trampoline_draw_bitmap_global as *const (),
658
  )?;
659
 
660
  let _ = hook::install(
661
  "DrawTextboxLocal",
662
+ va::RQ_DRAW_TEXTBOX_LOCAL,
663
  trampoline_draw_textbox_local as *const (),
664
  )?;
665
 
crates/openwa-wormkit/src/validation/mod.rs CHANGED
@@ -196,7 +196,7 @@ fn validate_addresses(result: &mut ValidationResult) {
196
  ("FireWeapon", va::FIRE_WEAPON),
197
  ("InitWeaponTable", va::INIT_WEAPON_TABLE),
198
  ("BlitScreen", va::BLIT_SCREEN),
199
- ("RenderDrawingQueue", va::RENDER_DRAWING_QUEUE),
200
  ("DrawLandscape", va::DRAW_LANDSCAPE),
201
  ("ConstructPCLandscape", va::CONSTRUCT_PC_LANDSCAPE),
202
  ("ConstructDSSound", va::CONSTRUCT_DS_SOUND),
 
196
  ("FireWeapon", va::FIRE_WEAPON),
197
  ("InitWeaponTable", va::INIT_WEAPON_TABLE),
198
  ("BlitScreen", va::BLIT_SCREEN),
199
+ ("RenderDrawingQueue", va::RQ_RENDER_DRAWING_QUEUE),
200
  ("DrawLandscape", va::DRAW_LANDSCAPE),
201
  ("ConstructPCLandscape", va::CONSTRUCT_PC_LANDSCAPE),
202
  ("ConstructDSSound", va::CONSTRUCT_DS_SOUND),
docs/re-notes/render-queue.md ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # RenderQueue System
2
+
3
+ ## Overview
4
+
5
+ WA.exe uses a deferred rendering queue. Game logic enqueues draw commands into a per-frame buffer, then `RQ_RenderDrawingQueue` (0x542350) processes them all at once, sorted by priority/layer.
6
+
7
+ The RenderQueue lives at DDGame+0x524 (see `ddgame_offsets::RENDER_QUEUE`).
8
+
9
+ ## RenderQueue struct (0x12008 bytes)
10
+
11
+ | Offset | Size | Field |
12
+ |--------|------|-------|
13
+ | 0x00000 | 4 | `buffer_offset` — write position (grows downward from 0x10000) |
14
+ | 0x00004 | 0x10000 | `_buffer` — command data storage |
15
+ | 0x10004 | 4 | `entry_count` — number of enqueued commands |
16
+ | 0x10008 | 0x2000 | `entry_ptrs[0x800]` — pointers to command entries |
17
+
18
+ Max 2048 (0x800) entries per frame. Buffer grows downward; each alloc subtracts from `buffer_offset`.
19
+
20
+ ## Command Types (0–14)
21
+
22
+ The dequeue function uses a switch on command type (first DWORD of each entry).
23
+
24
+ ### Enqueue functions (standalone, all ported to Rust)
25
+
26
+ | Type | Function | Address | DDDisplay vtable | Purpose |
27
+ |------|----------|---------|------------------|---------|
28
+ | 0 | RQ_DrawRect | 0x541F40 | [0x48] | Filled rectangle (clipped) |
29
+ | 1 | RQ_DrawBitmapGlobal | 0x542170 | [0x50] | Bitmap at world position |
30
+ | 2 | RQ_DrawTextboxLocal | 0x542200 | [0x50] | Text box (optional clipping) |
31
+ | 4 | RQ_DrawSpriteGlobal | 0x541FE0 | [0x4C] | Sprite at world position |
32
+ | 5 | RQ_DrawSpriteLocal | 0x542060 | [0x4C] | Sprite at screen position (camera-adjusted) |
33
+ | 6 | RQ_DrawSpriteOffset | 0x5420E0 | [0x4C] | Sprite with optional clipping/rotation |
34
+ | 8 | RQ_DrawLineStrip | 0x541DD0 | [0x38] | Connected line segments (variable-size) |
35
+ | 9 | RQ_DrawPolygon | 0x541E50 | [0x34] | Filled polygon edges (variable-size) |
36
+ | 0xB | RQ_DrawScaled | 0x541ED0 | [0x40] | Scaled sprite |
37
+ | 0xD | RQ_DrawPixel | 0x541D60 | [0x2C] | Single pixel |
38
+ | 0xE | RQ_DrawClippedSprite_Maybe | 0x5422A0 | [0x58] | Sprite with complex clipping (like type 6 but different vtable slot) |
39
+
40
+ ### Types without standalone enqueue functions
41
+
42
+ These types exist in the dequeue switch but have no standalone enqueue function in the binary. Their enqueue code is either inlined at call sites or unused in WA 3.8.1.
43
+
44
+ | Type | DDDisplay vtable | Dequeue behavior |
45
+ |------|------------------|------------------|
46
+ | 3 | [0x54] | Coord-clipped, 5 params |
47
+ | 7 | [0x30] | Direct command struct pass, 3 params |
48
+ | 10 | [0x3C] | Camera-adjusted + pixel offset, 6 params |
49
+ | 12 | [0x44] | Like type 0xB but different vtable slot |
50
+
51
+ ## DDDisplay Drawing Vtable
52
+
53
+ The dequeue function dispatches draw calls through a DDDisplay vtable. Slot offsets:
54
+
55
+ | Offset | Method | Used by types |
56
+ |--------|--------|---------------|
57
+ | 0x2C | PutPixel | 0xD |
58
+ | 0x30 | (unknown) | 7 |
59
+ | 0x34 | DrawPolygonEdge | 9 |
60
+ | 0x38 | DrawLineSegment | 8 |
61
+ | 0x3C | (unknown) | 10 |
62
+ | 0x40 | DrawScaledSprite | 0xB |
63
+ | 0x44 | (unknown) | 12 |
64
+ | 0x48 | DrawClippedRect | 0 |
65
+ | 0x4C | DrawSprite | 4, 5, 6 |
66
+ | 0x50 | DrawBitmap | 1, 2 |
67
+ | 0x54 | (unknown) | 3 |
68
+ | 0x58 | DrawClippedSprite | 0xE |
69
+
70
+ ## Helper Functions
71
+
72
+ | Address | Name | Purpose |
73
+ |---------|------|---------|
74
+ | 0x542B10 | RQ_GetCameraOffset_Maybe | Returns camera/viewport offset for screen-space conversion |
75
+ | 0x542BA0 | RQ_ClipCoordinates | Main 16.16 fixed-point coordinate clipping |
76
+ | 0x542C70 | RQ_ClipWithRefOffset_Maybe | Clipping with reference offset transform (type 6 flag 0x04) |
77
+ | 0x542D50 | RQ_TransformWithZoom_Maybe | Coordinate transformation with zoom/scale |
78
+ | 0x542E60 | RQ_SmoothInterpolate_Maybe | Linear interpolation with threshold (smooth scrolling) |
79
+ | 0x542F10 | RQ_UpdateClipBounds_Maybe | Viewport bounding box constraint updater |
80
+ | 0x542F70 | RQ_SaturateClipBounds_Maybe | Clipping with overflow saturation |
81
+
82
+ ## Render Pipeline
83
+
84
+ | Address | Name | Role |
85
+ |---------|------|------|
86
+ | 0x56E040 | RenderFrame_Maybe | Top-level render frame dispatcher |
87
+ | 0x533DC0 | GameRender_Maybe | Core game render: resets queue, processes scene, calls RQ_RenderDrawingQueue |
88
+ | 0x535000 | RenderTerrain_Maybe | Terrain/landscape viewport rendering |
89
+ | 0x534F20 | RenderHUD_Maybe | HUD/timer display |
90
+ | 0x534E00 | RenderTurnStatus_Maybe | Turn state/status text |
91
+ | 0x533C80 | PaletteManage_Maybe | Palette/color management |
92
+ | 0x533A80 | PaletteAnimate_Maybe | Palette animation/cycling (3 color palettes) |
93
+
94
+ ## Higher-Level Drawing Functions
95
+
96
+ These are not RenderQueue enqueue methods but call them:
97
+
98
+ | Address | Name | Notes |
99
+ |---------|------|-------|
100
+ | 0x500720 | DrawBungeeTrail | Uses DrawSpriteLocal + DrawPolygon/DrawLineStrip |
101
+ | 0x5197D0 | DrawCrosshairLine | Uses DrawPolygon + DrawSpriteLocal |
102
+
103
+ ## Dequeue Processing (0x542350)
104
+
105
+ `RQ_RenderDrawingQueue` sorts entries by priority field (DWORD at entry+4) via `qsort`, then iterates in reverse order (highest priority last = drawn on top). Each command is dispatched through the type switch, with coordinate clipping applied before calling the DDDisplay vtable method.
106
+
107
+ Fixed-point coordinates (16.16) are right-shifted to pixel values (`>> 0x10`) before passing to DDDisplay methods.