wormz-a / crates /openwa-core /src /engine /ddgame_wrapper.rs
paavohtl's picture
Claude Opus 4.6 (1M context)
refactor: port GfxDir resource loading system to Rust
3f42fbe
Raw History Blame Contribute Delete
11.7 kB
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);