File size: 11,742 Bytes
3f42fbe 04e939e 8790ac2 86781eb 5c850f4 8790ac2 d439705 123d0d1 f572faf 085ae61 4d4fb5b ed63829 4d4fb5b ed63829 4d4fb5b ed63829 d439705 123d0d1 ed63829 d439705 ed63829 f2fa062 d439705 314b694 d439705 314b694 d439705 c0e3602 d439705 314b694 d439705 4d4fb5b f572faf 86781eb d439705 7cb030a 4f85ac3 7cb030a 4f85ac3 4d4fb5b ed63829 4d4fb5b 1117dc5 88857ea 323b7e5 bee20b7 d439705 085ae61 4d4fb5b 902aa11 ed63829 902aa11 | 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 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | use crate::asset::gfx_dir::GfxDir;
use crate::audio::dssound::DSSound;
use crate::engine::ddgame::DDGame;
use crate::engine::net_bridge::NetBridge;
use crate::render::display::gfx::DisplayGfx;
use crate::render::landscape::PCLandscape;
use crate::render::palette::PaletteContext;
use crate::FieldRegistry;
/// Speech name table entry size (0x40 = 64 bytes, null-terminated C string).
pub const SPEECH_NAME_ENTRY_SIZE: usize = 0x40;
/// Maximum number of speech name entries.
pub const SPEECH_NAME_TABLE_LEN: usize = 360;
/// DDGameWrapper β large wrapper around DDGame.
///
/// Created by DDGameWrapper__Constructor (0x56DEF0).
/// Holds the DDGame pointer, graphics handlers, landscape, and display state.
///
/// Vtable: 0x66A30C
///
/// Note: Ghidra shows DWORD-indexed offsets (param_2[0x122] etc.).
/// Byte offset = dword_index * 4.
///
/// PARTIAL: Only confirmed fields are defined. Many fields in the
/// `_unknown_*` blobs are accessed via pointer arithmetic in game_state_init.rs
/// (team arrays at 0x054/0x128/0x22C, alliance data at 0x350/0x3AC, etc.).
#[derive(FieldRegistry)]
#[repr(C)]
pub struct DDGameWrapper {
// ===== 0x000: Vtable =====
/// 0x000: Vtable pointer (0x66A30C)
pub vtable: *mut u8,
/// 0x004: Unknown (4 bytes gap)
pub _unknown_004: u32,
// ===== 0x008-0x050: Sub-object pointers (allocated by InitGameState) =====
/// 0x008: CTaskTurnGame or CTaskGameState pointer
pub task_turn_game: *mut u8,
/// 0x00C: Main serialization BufferObject
pub main_buffer: *mut u8,
/// 0x010: Unknown pointer
pub _field_010: u32,
/// 0x014: Render BufferObject B (capacity 0x10000)
pub render_buffer_b: *mut u8,
/// 0x018: DisplayGfx layer A
pub display_gfx_a: *mut u8,
/// 0x01C: DisplayGfx layer B
pub display_gfx_b: *mut u8,
/// 0x020: DisplayGfx layer C
pub display_gfx_c: *mut u8,
/// 0x024: DisplayGfx main (ConstructFull result)
pub display_gfx_main: *mut u8,
/// 0x028: RingBuffer B (capacity 0x2000)
pub ring_buffer_b: *mut u8,
/// 0x02C: DisplayGfx D
pub display_gfx_d: *mut u8,
/// 0x030: Camera object A (0x3D4 bytes)
pub camera_a: *mut u8,
/// 0x034: DisplayGfx E
pub display_gfx_e: *mut u8,
/// 0x038: Camera object B (0x3D4 bytes)
pub camera_b: *mut u8,
/// 0x03C: RingBuffer A (capacity 0x2000)
pub ring_buffer_a: *mut u8,
/// 0x040: Render BufferObject A (capacity 0x10000)
pub render_buffer_a: *mut u8,
/// 0x044: Network RingBuffer (conditional on online mode)
pub network_ring_buffer: *mut u8,
/// 0x048: State serialization BufferObject
pub state_buffer: *mut u8,
/// 0x04C: Statistics object (0xB94 bytes)
pub statistics: *mut u8,
/// 0x050: RingBuffer C (capacity 0x1000)
pub ring_buffer_c: *mut u8,
// ===== 0x054-0x087: Per-team CTask pointers =====
/// 0x054: Per-team CTask pointers (13 slots). Zeroed by InitGameState.
/// Sub-fields +0x08..+0x18 cleared if non-null during InitTeamScoring.
pub team_task_ptrs: [*mut u8; 13],
// ===== 0x088-0x090: PaletteContext pointers =====
/// 0x088: PaletteContext A (allocated 0x72C bytes)
pub palette_ctx_a: *mut PaletteContext,
/// 0x08C: PaletteContext B (allocated 0x72C bytes)
pub palette_ctx_b: *mut PaletteContext,
/// 0x090: PaletteContext C (allocated 0x72C bytes)
pub palette_ctx_c: *mut PaletteContext,
// ===== 0x094-0x0F3: State fields =====
/// 0x094: Unknown gap
pub _unknown_094: u32,
/// 0x098-0x0CF: State block (14 u32s zeroed by InitGameState)
pub _zeroed_098: [u32; 14],
/// 0x0D0-0x0DF: Unknown
pub _unknown_0d0: [u8; 0x10],
/// 0x0E0: State flag (zeroed by InitGameState)
pub _field_0e0: u32,
/// 0x0E4-0x0EB: Unknown
pub _unknown_0e4: [u8; 8],
/// 0x0EC: State flag
pub _field_0ec: u32,
/// 0x0F0: Init flag (set to 1 by InitGameState early)
pub init_flag: u32,
// ===== 0x0F4-0x25F: Team scoring arrays (7 parallel arrays of 13 u32s) =====
/// 0x0F4: Team score array 0 β zeroed by InitTeamScoring.
pub team_score_array_0: [u32; 13],
/// 0x128: Starting team marker β 1 for starting team, 0 for others.
pub team_starting_marker: [u32; 13],
/// 0x15C: Team scoring A β initialized to scoring_param_a Γ 50.
pub team_scoring_a: [u32; 13],
/// 0x190: Team score array 3 β zeroed by InitTeamScoring.
pub team_score_array_3: [u32; 13],
/// 0x1C4: Team scoring B β initialized to scoring_param_b Γ 50.
pub team_scoring_b: [u32; 13],
/// 0x1F8: Team scoring C β initialized to scoring_param_b Γ 50.
pub team_scoring_c: [u32; 13],
/// 0x22C: Team activity flags β -1 (normal), -2 (training), 0 (inactive), 1 (starting).
pub team_activity_flags: [u32; 13],
// ===== 0x260-0x267: Game config =====
/// 0x260: Health display precision (initialized to 500)
pub health_precision: i32,
/// 0x264: Zeroed by InitGameState
pub _field_264: u32,
// ===== 0x268-0x273: Sync checksums (already defined) =====
/// 0x268: Network sync checksum A (written by GameFrameChecksumProcessor)
pub sync_checksum_a: u32,
/// 0x26C: Network sync checksum B (written by GameFrameChecksumProcessor)
pub sync_checksum_b: u32,
/// 0x270: Checksum validity flag (set to 1 after computation)
pub checksum_valid: u32,
// ===== 0x274-0x487: Game state fields =====
/// 0x274: Initial state checksum (computed at end of InitGameState)
pub initial_checksum: u32,
/// 0x278-0x27F: Zeroed
pub _field_278: u32,
pub _field_27c: u32,
/// 0x280-0x29C: Team render order indices (resolution-dependent, 8 entries)
pub team_render_indices: [i32; 8],
/// 0x2A0: Worm selection count
pub worm_select_count: i32,
/// 0x2A4: Worm selection count (alternate)
pub worm_select_count_alt: i32,
/// 0x2A8: Minimum number of active teams
pub min_active_teams: i32,
/// 0x2AC: Team count configuration (7 or 10)
pub team_count_config: i32,
/// 0x2B0: Maximum team render index (0xC or 0x10)
pub max_team_render_index: i32,
/// 0x2B4-0x2BB: Unknown.
pub _unknown_2b4: [u8; 8],
/// 0x2BC: Team score array 6 β set to 1 by InitTeamScoring.
pub team_score_array_6: [u32; 13],
/// 0x2F0-0x34F: Unknown.
pub _unknown_2f0: [u8; 0x350 - 0x2F0],
/// 0x350-0x383: Alliance bitmask arrays (13 Γ u32, accessed by init_alliance_data)
pub _alliance_bitmasks: [u32; 13],
/// 0x384: Screen offset (computed from screen height and team count)
pub screen_offset: i32,
/// 0x388: Screen height for HUD
pub screen_height_hud: i32,
/// 0x38C-0x3AB: Team-to-slot mapping (first half, 8 entries, filled with team indices)
pub team_to_slot_a: [i32; 8],
/// 0x3AC-0x3EB: Slot-to-team reverse mapping (16 entries, sentinel -1 init)
pub slot_to_team: [i32; 16],
/// 0x3EC: Last entry of sentinel block
pub _field_3ec: i32,
/// 0x3F0: Game mode flag (initialized to 1)
pub game_mode_flag: i32,
/// 0x3F4: Sentinel -1
pub _field_3f4: i32,
/// 0x3F8: Sentinel -1
pub _field_3f8: i32,
/// 0x3FC: Zeroed
pub _field_3fc: i32,
/// 0x400: Game turn timer (max_team_render_index << 5)
pub turn_timer_max: i32,
/// 0x404: Zeroed
pub _field_404: i32,
/// 0x408: Game turn timer copy
pub turn_timer_current: i32,
/// 0x40C-0x413: Zeroed
pub _field_40c: i32,
pub _field_410: i32,
/// 0x414: Config bool (from game_info)
pub _field_414: u32,
/// 0x418: Zeroed
pub _field_418: i32,
/// 0x41C: Unknown
pub _unknown_41c: u32,
/// 0x420: Turn percentage (from game_info, fixed-point)
pub turn_percentage: i32,
/// 0x424-0x44F: State fields (mix of zeroes and values)
pub _field_424: i32,
pub _field_428: i32,
pub _field_42c: i32,
pub _field_430: i32,
/// 0x434: Zeroed
pub _field_434: i32,
/// 0x438: Sentinel -1
pub _field_438: i32,
/// 0x43C: Sentinel -1
pub _field_43c: i32,
/// 0x440: Zeroed
pub _field_440: i32,
/// 0x444: Zeroed
pub _field_444: i32,
/// 0x448: Zeroed
pub _field_448: i32,
/// 0x44C: Zeroed
pub _field_44c: i32,
/// 0x450-0x45B: Turn state fields (written by init_turn_state)
pub _field_450: u32,
pub _field_454: u32,
pub _field_458: u32,
/// 0x45C: Zeroed
pub _field_45c: i32,
/// 0x460: Zeroed
pub _field_460: i32,
/// 0x464: Zeroed
pub _field_464: i32,
/// 0x468: Sentinel -1
pub _field_468: i32,
/// 0x46C: Sentinel -1
pub _field_46c: i32,
/// 0x470: Sentinel -1
pub _field_470: i32,
/// 0x474: Zeroed
pub _field_474: u32,
/// 0x478: Zeroed
pub _field_478: u32,
/// 0x47C: Unknown
pub _unknown_47c: u32,
/// 0x480: Zeroed
pub _field_480: u32,
/// 0x484: State initialized flag (set to 1 at end of InitGameState)
pub state_initialized: u32,
// ===== 0x488-0x4BF: Core pointers and flags =====
/// 0x488: Pointer to DDGame allocation (DWORD index 0x122)
pub ddgame: *mut DDGame,
/// 0x48C: Network bridge object (0x2C bytes). Only set for online games (game_version == -2).
pub net_bridge: *mut NetBridge,
/// 0x490: Replay flag A (from game_info)
pub replay_flag_a: u8,
/// 0x491: Replay flag B (from game_info)
pub replay_flag_b: u8,
/// 0x492: Unknown byte
pub _unknown_492: u8,
/// 0x493: Zeroed by InitGameState
pub _field_493: u8,
/// 0x494: Zeroed by InitGameState
pub _field_494: u32,
/// 0x498: Zeroed by InitGameState
pub _field_498: u32,
/// 0x49C: Version counter (read by InitGameState, not written)
pub _field_49c: u32,
/// 0x4A0-0x4AB: Unknown
pub _unknown_4a0: [u8; 0x0C],
/// 0x4AC: State field
pub _field_4ac: u32,
/// 0x4B0: Sentinel -1
pub _field_4b0: u32,
/// 0x4B4-0x4BF: Unknown
pub _unknown_4b4: [u8; 0x0C],
// ===== 0x4C0-0x4E3: Graphics/display/sound pointers =====
/// 0x4C0: Primary GfxDir β main sprite archive (Gfx.dir / Gfx0.dir / Gfx1.dir).
pub primary_gfx_dir: *mut GfxDir,
/// 0x4C4: Secondary GfxDir β supplemental sprites (GfxC_3_0.dir), conditional on game version.
pub secondary_gfx_dir: *mut GfxDir,
/// 0x4C8: Graphics mode flag (DWORD index 0x132)
pub gfx_mode: u32,
/// 0x4CC: PCLandscape object pointer (DWORD index 0x133)
pub landscape: *mut PCLandscape,
/// 0x4D0: DisplayGfx pointer (param2 of constructor)
pub display: *mut DisplayGfx,
/// 0x4D4: DSSound pointer (param3 of constructor)
pub sound: *mut DSSound,
/// 0x4D8: Loading progress counter (incremented per loading tick).
pub loading_progress: u32,
/// 0x4DC: Loading progress total (base 0x2AD + 0x38 per team + 0x7E overhead).
pub loading_total: u32,
/// 0x4E0: Last displayed loading percentage (init -100 to force first update).
pub loading_last_pct: u32,
// ===== 0x4E4-0x6F0F: Remaining fields =====
/// 0x4E4-0x14E7: Unknown fields
pub _unknown_4e4: [u8; 0x14E8 - 0x4E4],
/// 0x14E8: Speech name table β 360 entries of 0x40-byte C strings.
/// Used by DDGameWrapper__LoadSpeechWAV to deduplicate loaded WAVs.
pub speech_name_table: [[u8; SPEECH_NAME_ENTRY_SIZE]; SPEECH_NAME_TABLE_LEN],
/// 0x6EE8: Number of entries used in speech_name_table.
pub speech_name_count: u32,
/// 0x6EEC: Init 0 (DWORD index 0x1BBA)
pub _field_6eec: u32,
/// 0x6EF0-0x6F0F: Unknown trailing fields (not zeroed by constructor memset).
pub _unknown_6ef0: [u8; 0x20],
}
const _: () = assert!(core::mem::size_of::<DDGameWrapper>() == 0x6F10);
|