paavohtl Claude Opus 4.6 commited on
Commit
4d4fb5b
·
1 Parent(s): ed63829

Correct DDGame field offsets from constructor decompilation

Browse files

Major fixes from analyzing the full 884-line DDGame__Constructor:
- task_ptrs were at wrong offset (0x11B0 vs correct 0x46C-0x488)
- Map 0x000-0x510 range densely: arrows, sprite regions, collision
- DDGameWrapper is ~28KB+ (not 0x500), accesses DWORD 0x1BBA
- Add 5 new function addresses: SpriteRegion ctor, GfxHandler/Dir ops
- Rename 5 functions in Ghidra (SpriteRegion__Constructor, etc.)

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

crates/openwa-types/src/address.rs CHANGED
@@ -140,10 +140,15 @@ pub mod va {
140
  pub const LOAD_SPRITE: u32 = 0x0052_3400;
141
  pub const CONSTRUCT_OPENGL_CPU: u32 = 0x005A_0850;
142
  pub const OPENGL_INIT: u32 = 0x0059_F000;
 
 
 
 
143
 
144
  // === Landscape ===
145
 
146
  pub const CONSTRUCT_PC_LANDSCAPE: u32 = 0x0057_ACB0;
 
147
  pub const REDRAW_LAND_REGION: u32 = 0x0057_CC10;
148
  pub const WRITE_LAND_RAW: u32 = 0x0057_C300;
149
 
 
140
  pub const LOAD_SPRITE: u32 = 0x0052_3400;
141
  pub const CONSTRUCT_OPENGL_CPU: u32 = 0x005A_0850;
142
  pub const OPENGL_INIT: u32 = 0x0059_F000;
143
+ pub const DDGAME_INIT_FIELDS: u32 = 0x0052_6120;
144
+ pub const GFX_HANDLER_LOAD_DIR: u32 = 0x0056_63E0;
145
+ pub const GFX_DIR_FIND_ENTRY: u32 = 0x0056_6520;
146
+ pub const GFX_DIR_LOAD_IMAGE: u32 = 0x0056_66D0;
147
 
148
  // === Landscape ===
149
 
150
  pub const CONSTRUCT_PC_LANDSCAPE: u32 = 0x0057_ACB0;
151
+ pub const CONSTRUCT_SPRITE_REGION: u32 = 0x0057_DB20;
152
  pub const REDRAW_LAND_REGION: u32 = 0x0057_CC10;
153
  pub const WRITE_LAND_RAW: u32 = 0x0057_C300;
154
 
crates/openwa-types/src/ddgame.rs CHANGED
@@ -6,98 +6,123 @@ use crate::task::Ptr32;
6
  /// display, landscape, sound, graphics handlers, and task state machines.
7
  ///
8
  /// Allocated in DDGame__Constructor (0x56E220).
9
- /// Vtable set via DDGameWrapper at 0x66A30C.
10
  ///
11
- /// PARTIAL: Only landmark offsets are defined. The vast majority of fields
12
- /// are unknown. Use the offset constants below for field access.
13
  ///
14
- /// Note: Ghidra decompiles DDGame constructor with DWORD-indexed offsets
15
- /// (param_2[0x122] etc.), but these are byte offsets in the struct below.
16
- /// Conversion: dword_index * 4 = byte_offset.
17
  #[repr(C)]
18
  pub struct DDGame {
19
- /// 0x0000-0x0023: Header region
20
- pub _header: [u8; 0x24],
21
- /// 0x0024: Game state pointer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  pub game_state: Ptr32,
23
- /// 0x0028: Unknown parameter from constructor
24
- pub _param_28: Ptr32,
25
- /// 0x002C-0x11AF: Unknown fields
26
- pub _unknown_002c: [u8; 0x1184],
27
- /// 0x11B0-0x11BF: Task objects region (5 state machine pointers)
28
- /// Ghidra: param[0x46C..0x47C] (dword-indexed)
29
- pub task_ptrs: [Ptr32; 5],
30
- /// 0x11C4-0x1217: Unknown
31
- pub _unknown_11c4: [u8; 0x54],
32
- /// 0x1218-0x1223: Game global / WormKit-known offsets area
33
- /// Note: WormKit documents DDGame+0x488 as game_global, but that's
34
- /// relative to the DDGame ptr held in DDGameWrapper[0x122].
35
- /// Since DDGameWrapper[0x122] points to THIS struct at offset 0,
36
- /// these WormKit offsets map directly.
37
- pub _unknown_1218: [u8; 0x270],
38
- /// 0x1488-0x3547: Unknown fields
39
- pub _unknown_1488: [u8; 0x20C0],
40
- /// 0x3548: Display mode pointer
41
- pub display_mode: Ptr32,
42
- /// 0x354C: Display width
43
- pub display_width: u32,
44
- /// 0x3550: Display param (init 0)
45
- pub _display_3550: u32,
46
- /// 0x3554: Display param (init 0)
47
- pub _display_3554: u32,
48
- /// 0x3558-0x355F: Unknown
49
- pub _unknown_3558: [u8; 8],
50
- /// 0x3560: Display center X (width / 2)
51
- pub display_center_x: u32,
52
- /// 0x3564: Display center Y (height / 2)
53
- pub display_center_y: u32,
54
- /// 0x3568-0x3577: Unknown
55
- pub _unknown_3568: [u8; 0x10],
56
- /// 0x3578: Window handle (HWND)
57
- pub hwnd: Ptr32,
58
- /// 0x357C-0x358C: Unknown
59
- pub _unknown_357c: [u8; 0x11],
60
- /// 0x358D-0x398C: Palette entries (0x100 * 4 = 1024 bytes)
61
- pub palette: [u8; 0x400],
62
- /// 0x398D-0x3D8F: Unknown display fields
63
- pub _unknown_398d: [u8; 0x403],
64
- /// 0x3D90: Graphics constant (init 0x100)
65
- pub _gfx_3d90: u32,
66
- /// 0x3D94: Graphics constant (init 0xFFFFFFFF)
67
- pub _gfx_3d94: u32,
68
- /// 0x3D98: Graphics object pointers (4 entries)
69
- pub gfx_objects: [Ptr32; 4],
70
- /// 0x3DA8-0x98B7: Remaining fields
71
- pub _unknown_3da8: [u8; 0x5B10],
72
  }
73
 
74
  const _: () = assert!(core::mem::size_of::<DDGame>() == 0x98B8);
75
 
76
  /// Well-known byte offsets into DDGame, for use with raw pointer access.
77
  ///
78
- /// These are sourced from WormKit, wkJellyWorm, and Ghidra decompilation.
79
- /// WormKit documents offsets relative to DDGameWrapper[0x122] which points
80
- /// to the DDGame allocation.
81
  pub mod offsets {
82
- /// Game state pointer
83
- pub const GAME_STATE: usize = 0x0024;
 
 
 
84
 
85
- /// Task state machine pointers (5 entries, each Ptr32)
86
- pub const TASK_PTRS: usize = 0x11B0;
 
 
87
 
88
- /// Display mode pointer
89
- pub const DISPLAY_MODE: usize = 0x3548;
90
- /// Display width
91
- pub const DISPLAY_WIDTH: usize = 0x354C;
92
- /// Display center X
93
- pub const DISPLAY_CENTER_X: usize = 0x3560;
94
- /// Display center Y
95
- pub const DISPLAY_CENTER_Y: usize = 0x3564;
96
- /// Window handle (HWND)
97
- pub const HWND: usize = 0x3578;
98
- /// Palette start (0x400 bytes)
99
- pub const PALETTE: usize = 0x358D;
100
 
101
- /// Graphics object pointers (from DDDisplay__Init)
102
- pub const GFX_OBJECTS: usize = 0x3D98;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  }
 
6
  /// display, landscape, sound, graphics handlers, and task state machines.
7
  ///
8
  /// Allocated in DDGame__Constructor (0x56E220).
9
+ /// The DDGame pointer is stored at DDGameWrapper+0x488 (DWORD index 0x122).
10
  ///
11
+ /// PARTIAL: Fields up to 0x510 are densely mapped from the constructor.
12
+ /// Beyond that, only scattered fields are known — use the `offsets` module.
13
  ///
14
+ /// Note on offsets: The constructor accesses DDGame fields via
15
+ /// `*(param_2[0x122] + byte_offset)` — these are byte offsets, NOT DWORD-indexed.
16
+ /// DWORD indexing only applies to param_2 (DDGameWrapper) itself.
17
  #[repr(C)]
18
  pub struct DDGame {
19
+ /// 0x000: Base value (param_5 from constructor)
20
+ pub _base_000: Ptr32,
21
+ /// 0x004: Context pointer (param_3)
22
+ pub _context: Ptr32,
23
+ /// 0x008: param_4
24
+ pub _param_008: Ptr32,
25
+ /// 0x00C: Allocated object (0x608 bytes, conditional)
26
+ pub _object_00c: Ptr32,
27
+ /// 0x010: param_6
28
+ pub _param_010: Ptr32,
29
+ /// 0x014: param_7
30
+ pub _param_014: Ptr32,
31
+ /// 0x018: param_8
32
+ pub _param_018: Ptr32,
33
+ /// 0x01C: Caller/parent pointer (param_1)
34
+ pub _caller: Ptr32,
35
+ /// 0x020: PCLandscape pointer (copied from DDGameWrapper[0x133])
36
+ pub landscape: Ptr32,
37
+ /// 0x024: Game state pointer (param_10)
38
  pub game_state: Ptr32,
39
+ /// 0x028: param_9
40
+ pub _param_028: Ptr32,
41
+ /// 0x02C: Secondary GfxDir object (0x70C bytes, conditional on GfxHandler 1)
42
+ pub secondary_gfxdir: Ptr32,
43
+ /// 0x030: Gradient image pointer
44
+ pub gradient_image: Ptr32,
45
+ /// 0x034: Gradient image 2 pointer
46
+ pub gradient_image_2: Ptr32,
47
+ /// 0x038-0x0B4: Arrow sprite object pointers (32 entries)
48
+ pub arrow_sprites: [Ptr32; 32],
49
+ /// 0x0B8-0x134: Arrow GfxDir pointers (32 entries, conditional)
50
+ pub arrow_gfxdirs: [Ptr32; 32],
51
+ /// 0x138: DisplayGfx object pointer (vtable 0x664144)
52
+ pub display_gfx: Ptr32,
53
+ /// 0x13C-0x37F: Unknown
54
+ pub _unknown_13c: [u8; 0x244],
55
+ /// 0x380: TaskStateMachine pointer (vtable 0x664118, 0x2C bytes)
56
+ pub task_state_machine: Ptr32,
57
+ /// 0x384-0x467: Unknown
58
+ pub _unknown_384: [u8; 0xE4],
59
+ /// 0x468: Landscape-derived value
60
+ pub _landscape_val: Ptr32,
61
+ /// 0x46C-0x488: 8 SpriteRegion pointers (0x9C bytes each, vtable 0x66B268)
62
+ /// Created by SpriteRegion__Constructor (0x57DB20).
63
+ /// Each contains 32 TaskStateMachine sub-objects.
64
+ pub sprite_regions: [Ptr32; 8],
65
+ /// 0x48C-0x508: Arrow collision region pointers (32 entries)
66
+ pub arrow_collision_regions: [Ptr32; 32],
67
+ /// 0x50C: Coordinate list object (capacity 600, 0x12C0 data buffer)
68
+ pub coord_list: Ptr32,
69
+ /// 0x510-0x98B7: Remaining fields (sparse — see offsets module)
70
+ ///
71
+ /// Known landmarks in this region:
72
+ /// - 0x64D8: cleared by init
73
+ /// - 0x72A4: cleared by init
74
+ /// - 0x730C-0x732C: 9 GfxDir color entries
75
+ /// - 0x7338: fill pixel value
76
+ /// - 0x77C4: display-related value
77
+ /// - 0x7EF8: flag from game_state+0xF914
78
+ /// - 0x7EFC: init 1
79
+ /// - 0x8CBC-0x8CF0: 4x 0x10-byte entries (zeroed at +0, +4)
80
+ /// - 0x9850-0x9884: 4x 0x10-byte entries (zeroed at +0, +4)
81
+ ///
82
+ /// Also includes FUN_00526120 zeroed offsets at stride 0x194:
83
+ /// 0x379C, 0x3930, 0x3AC4, 0x3C58, 0x3DEC, 0x3F80, 0x4114, 0x42A8, 0x443C, 0x45D0
84
+ pub _remaining: [u8; 0x93A8],
 
 
 
85
  }
86
 
87
  const _: () = assert!(core::mem::size_of::<DDGame>() == 0x98B8);
88
 
89
  /// Well-known byte offsets into DDGame, for use with raw pointer access.
90
  ///
91
+ /// The DDGame pointer is at DDGameWrapper+0x488 (DWORD index 0x122).
 
 
92
  pub mod offsets {
93
+ // === Header / init params (0x000-0x02C) ===
94
+ pub const LANDSCAPE: usize = 0x020;
95
+ pub const GAME_STATE: usize = 0x024;
96
+ pub const SECONDARY_GFXDIR: usize = 0x02C;
97
+ pub const GRADIENT_IMAGE: usize = 0x030;
98
 
99
+ // === Sprite arrays (0x038-0x138) ===
100
+ pub const ARROW_SPRITES: usize = 0x038;
101
+ pub const ARROW_GFXDIRS: usize = 0x0B8;
102
+ pub const DISPLAY_GFX: usize = 0x138;
103
 
104
+ // === Task/state machines (0x380-0x488) ===
105
+ pub const TASK_STATE_MACHINE: usize = 0x380;
106
+ pub const SPRITE_REGIONS: usize = 0x46C;
 
 
 
 
 
 
 
 
 
107
 
108
+ // === Arrow collision (0x48C-0x50C) ===
109
+ pub const ARROW_COLLISION_REGIONS: usize = 0x48C;
110
+ pub const COORD_LIST: usize = 0x50C;
111
+
112
+ // === WormKit-documented offsets ===
113
+ pub const WEAPON_TABLE: usize = 0x510;
114
+ pub const WEAPON_PANEL: usize = 0x548;
115
+
116
+ // === FUN_00526120 init offsets (stride 0x194, 10 entries) ===
117
+ pub const INIT_TABLE_BASE: usize = 0x379C;
118
+ pub const INIT_TABLE_STRIDE: usize = 0x194;
119
+
120
+ // === Sparse fields in upper region ===
121
+ pub const FIELD_64D8: usize = 0x64D8;
122
+ pub const FIELD_72A4: usize = 0x72A4;
123
+ pub const GFX_COLOR_ENTRIES: usize = 0x730C;
124
+ pub const FILL_PIXEL: usize = 0x7338;
125
+ pub const DISPLAY_77C4: usize = 0x77C4;
126
+ pub const FLAG_7EF8: usize = 0x7EF8;
127
+ pub const FIELD_7EFC: usize = 0x7EFC;
128
  }
crates/openwa-types/src/ddgame_wrapper.rs CHANGED
@@ -1,23 +1,27 @@
1
  use crate::task::Ptr32;
2
 
3
- /// DDGameWrapper — thin wrapper around DDGame.
4
  ///
5
- /// This is the top-level object created by DDGameWrapper__Constructor (0x56DEF0).
6
- /// It holds a pointer to the DDGame allocation and graphics handler references.
7
  ///
8
  /// Vtable: 0x66A30C
9
  ///
10
- /// Note: Ghidra shows DWORD-indexed offsets (param_1[0x122] etc.).
11
  /// Byte offset = dword_index * 4.
12
  ///
13
- /// PARTIAL: Only confirmed fields from the constructor are defined.
 
 
 
 
14
  #[repr(C)]
15
  pub struct DDGameWrapper {
16
  /// 0x000: Vtable pointer (0x66A30C)
17
  pub vtable: Ptr32,
18
  /// 0x004-0x487: Unknown fields
19
  pub _unknown_004: [u8; 0x484],
20
- /// 0x488: Pointer to DDGame allocation
21
  pub ddgame: Ptr32,
22
  /// 0x48C: Secondary DDGame struct pointer (0x2C bytes, conditional)
23
  pub ddgame_secondary: Ptr32,
@@ -27,19 +31,28 @@ pub struct DDGameWrapper {
27
  pub gfx_handler_0: Ptr32,
28
  /// 0x4C4: Graphics handler 1 pointer (optional)
29
  pub gfx_handler_1: Ptr32,
30
- /// 0x4C8: Graphics mode flag
31
  pub gfx_mode: u32,
32
- /// 0x4CC: PCLandscape object pointer
33
  pub landscape: Ptr32,
34
- /// 0x4D0: Constructor param_2
35
- pub _param_4d0: u32,
36
- /// 0x4D4: Constructor param_3
37
- pub _param_4d4: u32,
38
- /// 0x4D8-end: Unknown trailing fields
39
- /// Size uncertain — DDGameWrapper total size not yet determined
40
- pub _unknown_4d8: [u8; 0x28],
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
- // DDGameWrapper total size is uncertain. This is a minimum based on known fields.
44
- // Remove or update this assertion when the true size is determined.
45
- const _: () = assert!(core::mem::size_of::<DDGameWrapper>() == 0x500);
 
1
  use crate::task::Ptr32;
2
 
3
+ /// DDGameWrapper — large wrapper around DDGame.
4
  ///
5
+ /// Created by DDGameWrapper__Constructor (0x56DEF0).
6
+ /// Holds the DDGame pointer, graphics handlers, landscape, and display state.
7
  ///
8
  /// Vtable: 0x66A30C
9
  ///
10
+ /// Note: Ghidra shows DWORD-indexed offsets (param_2[0x122] etc.).
11
  /// Byte offset = dword_index * 4.
12
  ///
13
+ /// The constructor accesses at least up to DWORD index 0x1BBA (byte offset 0x6EE8),
14
+ /// making this object at least ~28KB. Total size is not yet determined.
15
+ ///
16
+ /// PARTIAL: Only confirmed fields are defined. The repr(C) struct uses
17
+ /// a conservative size that covers known fields. The actual object is larger.
18
  #[repr(C)]
19
  pub struct DDGameWrapper {
20
  /// 0x000: Vtable pointer (0x66A30C)
21
  pub vtable: Ptr32,
22
  /// 0x004-0x487: Unknown fields
23
  pub _unknown_004: [u8; 0x484],
24
+ /// 0x488: Pointer to DDGame allocation (DWORD index 0x122)
25
  pub ddgame: Ptr32,
26
  /// 0x48C: Secondary DDGame struct pointer (0x2C bytes, conditional)
27
  pub ddgame_secondary: Ptr32,
 
31
  pub gfx_handler_0: Ptr32,
32
  /// 0x4C4: Graphics handler 1 pointer (optional)
33
  pub gfx_handler_1: Ptr32,
34
+ /// 0x4C8: Graphics mode flag (DWORD index 0x132)
35
  pub gfx_mode: u32,
36
+ /// 0x4CC: PCLandscape object pointer (DWORD index 0x133)
37
  pub landscape: Ptr32,
38
+ /// 0x4D0: DDDisplay/display object pointer (DWORD index 0x134)
39
+ /// Has vtable methods at +0x10, +0x5C, +0x7C, +0x94
40
+ pub display: Ptr32,
41
+ /// 0x4D4: Unknown
42
+ pub _field_4d4: u32,
43
+ /// 0x4D8: Init 0 (DWORD index 0x136)
44
+ pub _field_4d8: u32,
45
+ /// 0x4DC: Calculated value (DWORD index 0x137)
46
+ pub _field_4dc: u32,
47
+ /// 0x4E0: Init -100 / 0xFFFFFF9C (DWORD index 0x138)
48
+ pub _field_4e0: u32,
49
+ /// 0x4E4-0x6EEB: Unknown fields (extends to at least DWORD 0x1BBA)
50
+ pub _unknown_4e4: [u8; 0x6A08],
51
+ /// 0x6EEC: Init 0 (DWORD index 0x1BBA)
52
+ pub _field_6eec: u32,
53
+ /// 0x6EF0-end: Unknown trailing fields
54
+ pub _unknown_6ef0: [u8; 0x10],
55
  }
56
 
57
+ // This is a minimum estimate. The actual object is likely larger.
58
+ const _: () = assert!(core::mem::size_of::<DDGameWrapper>() == 0x6F00);