wormz-a / crates /openwa-core /src /engine /game_session.rs
paavohtl's picture
Claude Opus 4.6 (1M context)
refactor: reorganize display/ and render/ into unified render/ module
5c850f4
Raw History Blame Contribute Delete
5.36 kB
use crate::audio::dssound::DSSound;
use crate::audio::music::Music;
use crate::engine::ddgame_wrapper::DDGameWrapper;
use crate::input::keyboard::DDKeyboard;
use crate::render::display::palette::Palette;
use crate::FieldRegistry;
/// Top-level game session context, allocated once per game run.
///
/// `G_GAME_SESSION` (0x7A0884) stores a pointer to this struct. Created by
/// `GameSession__Constructor` (0x58BFA0, usercall EAX=this), populated by
/// `GameEngine__InitHardware` (0x56D350), and destroyed by
/// `GameEngine__Shutdown` (0x56DCD0).
///
/// Lifecycle:
/// ```text
/// GameSession__Run (0x572F50, ESI=GameInfo)
/// β”œβ”€ alloc 0x120 bytes β†’ GameSession__Constructor (0x58BFA0)
/// β”œβ”€ G_GAME_SESSION ← &this
/// β”œβ”€ GameEngine__InitHardware β†’ fills subsystem pointers (0xA0–0xC0)
/// β”œβ”€ game main loop (exit when exit_flag != 0)
/// └─ GameEngine__Shutdown β†’ destroys all subsystems
/// ```
///
/// In headless/stats mode (`GameInfo+0xF914 != 0`), `display_gfx` holds a
/// `GameStats` object with a DDInput vtable instead of a real `DisplayGfx`.
#[derive(FieldRegistry)]
#[repr(C)]
pub struct GameSession {
/// 0x000: vtable pointer (class `GameSession`, `PTR_FUN_0066b3f8`)
pub vtable: *mut u8,
pub _unknown_004: [u8; 4],
/// 0x008: main window HWND, stored from `hWnd` in `GameSession__Run`
pub hwnd: u32,
pub _unknown_00c: [u8; 0x18],
/// 0x024: Always set to 1 by GameEngine__InitHardware.
pub init_flag: u32,
/// 0x028: Fullscreen flag β€” `(GameInfo.home_lock != 0) as u32`.
pub fullscreen_flag: u32,
/// 0x02C: set to 1 on init (role TBD)
pub flag_2c: u32,
pub _unknown_030: [u8; 8],
/// 0x038: back-pointer to the `GameInfo` config struct (ESI from `GameSession__Run`)
pub config_ptr: *mut u8,
/// 0x03C: nonzero = exit the game main loop (checked each frame in `GameSession__Run`)
pub exit_flag: u32,
/// 0x040: game-end status/result code
pub flag_40: u32,
pub _unknown_044: [u8; 0x10],
/// 0x054: display center X = `display_width / 2`.
/// Initialized to `0x80000000` by `GameSession__Constructor`, then overwritten.
pub screen_center_x: i32,
/// 0x058: display center Y = `display_height / 2`
pub screen_center_y: i32,
pub _unknown_05c: [u8; 0x14],
/// 0x070: cursor X at session start (from `GetCursorPos`)
pub cursor_initial_x: i32,
/// 0x074: cursor Y at session start
pub cursor_initial_y: i32,
pub _unknown_078: [u8; 8],
/// 0x080: cursor center X β€” set to `screen_center_x`, used for `SetCursorPos`
pub cursor_x: i32,
/// 0x084: cursor center Y
pub cursor_y: i32,
pub _unknown_088: [u8; 8],
/// 0x090: `QueryPerformanceFrequency` result low DWORD (0 if QPC unavailable)
pub timer_freq_lo: u32,
/// 0x094: high DWORD
pub timer_freq_hi: u32,
/// 0x098: QPC counter accumulator low DWORD
pub timer_counter_lo: u32,
/// 0x09C: high DWORD
pub timer_counter_hi: u32,
/// 0x0A0: `DDGameWrapper*` β€” the main game object wrapper (β†’ `DDGame` at `+0x488`)
pub ddgame_wrapper: *mut DDGameWrapper,
/// 0x0A4: `DDKeyboard*` β€” 0x33C bytes, vtable `DDKeyboard_vtable` (0x66AEC8)
pub keyboard: *mut DDKeyboard,
/// 0x0A8: `DSSound*` β€” 0xBE0 bytes, vtable `DSSound_vtable` (0x66AF20)
pub sound: *mut DSSound,
/// 0x0AC: Polymorphic display β€” `DisplayGfx*` (normal) or `DisplayBase*` (headless).
/// Stays `*mut u8` because the concrete type depends on mode.
pub display: *mut u8,
/// 0x0B0: `Palette*` β€” 0x28 bytes, vtable `Palette_vtable_Maybe`
pub palette: *mut Palette,
/// 0x0B4: Music object β€” 0x354 bytes (constructor 0x58BC10, vtable 0x66B3E0).
/// Combines playlist controller + embedded streaming audio engine.
pub streaming_audio: *mut Music,
/// 0x0B8: input controller β€” 0x1800 bytes; null if `param_4 == 0` at init
pub input_ctrl: *mut u8,
/// 0x0BC: timing object β€” 0x30 bytes (`FUN_0053e950`)
pub timer_obj: *mut u8,
/// 0x0C0: `DDNetGameWrapper*` β€” 0x2C bytes
pub net_game: *mut u8,
pub _unknown_0c4: [u8; 0x5C],
}
const _: () = assert!(core::mem::size_of::<GameSession>() == 0x120);
// ─── Runtime accessors (DLL-injected context only) ──────────────────────
#[cfg(target_arch = "x86")]
use crate::address::va;
#[cfg(target_arch = "x86")]
use crate::engine::ddgame::DDGame;
#[cfg(target_arch = "x86")]
use crate::rebase::rb;
/// Get the DDGameWrapper pointer from the global game session.
///
/// Returns null if the session or wrapper hasn't been initialized yet.
#[cfg(target_arch = "x86")]
#[inline]
pub unsafe fn get_wrapper() -> *mut DDGameWrapper {
let session = *(rb(va::G_GAME_SESSION) as *const *mut GameSession);
if session.is_null() {
return core::ptr::null_mut();
}
(*session).ddgame_wrapper
}
/// Get the DDGame pointer from the global game session.
///
/// Follows the chain: G_GAME_SESSION β†’ GameSession.ddgame_wrapper β†’ DDGameWrapper.ddgame.
/// Returns null if any link in the chain is uninitialized.
#[cfg(target_arch = "x86")]
#[inline]
pub unsafe fn get_ddgame() -> *mut DDGame {
let wrapper = get_wrapper();
if wrapper.is_null() {
return core::ptr::null_mut();
}
(*wrapper).ddgame
}