Correct DDGame field offsets from constructor decompilation
Browse filesMajor 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 |
-
///
|
| 10 |
///
|
| 11 |
-
/// PARTIAL:
|
| 12 |
-
///
|
| 13 |
///
|
| 14 |
-
/// Note
|
| 15 |
-
/// (param_2[0x122]
|
| 16 |
-
///
|
| 17 |
#[repr(C)]
|
| 18 |
pub struct DDGame {
|
| 19 |
-
///
|
| 20 |
-
pub
|
| 21 |
-
///
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
pub game_state: Ptr32,
|
| 23 |
-
///
|
| 24 |
-
pub
|
| 25 |
-
///
|
| 26 |
-
pub
|
| 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 |
-
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 |
-
///
|
| 79 |
-
/// WormKit documents offsets relative to DDGameWrapper[0x122] which points
|
| 80 |
-
/// to the DDGame allocation.
|
| 81 |
pub mod offsets {
|
| 82 |
-
///
|
| 83 |
-
pub const
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
-
//
|
| 86 |
-
pub const
|
|
|
|
|
|
|
| 87 |
|
| 88 |
-
///
|
| 89 |
-
pub const
|
| 90 |
-
|
| 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 |
-
//
|
| 102 |
-
pub const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 —
|
| 4 |
///
|
| 5 |
-
///
|
| 6 |
-
///
|
| 7 |
///
|
| 8 |
/// Vtable: 0x66A30C
|
| 9 |
///
|
| 10 |
-
/// Note: Ghidra shows DWORD-indexed offsets (
|
| 11 |
/// Byte offset = dword_index * 4.
|
| 12 |
///
|
| 13 |
-
///
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
///
|
| 40 |
-
pub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
}
|
| 42 |
|
| 43 |
-
//
|
| 44 |
-
|
| 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);
|
|
|