feat: create_ddgame() with bridges for first 60% of constructor
Browse filesImplement create_ddgame() covering: allocation, zero-fill, InitFields,
param storage, version flags, GfxHandler creation, display palette,
color entries, secondary GfxDir, audio init, PCLandscape bridge,
TaskStateMachine, 8x SpriteRegion bridges, landscape value.
Not yet wired in β game_session.rs still calls the original DDGame
constructor via bridge. create_ddgame() will replace it once the
remaining sections (arrow sprites, coord list, gradients, weapons,
HUD, display finalization) are ported.
15 new sub-function addresses, SpriteRegion fastcall bridge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -369,6 +369,7 @@ static mut INIT_FIELDS_ADDR: u32 = 0;
|
|
| 369 |
pub fn init_constructor_addrs() {
|
| 370 |
unsafe {
|
| 371 |
INIT_FIELDS_ADDR = rb(va::DDGAME_INIT_FIELDS);
|
|
|
|
| 372 |
}
|
| 373 |
}
|
| 374 |
|
|
@@ -692,13 +693,166 @@ unsafe fn init_graphics_and_resources(
|
|
| 692 |
}
|
| 693 |
}
|
| 694 |
|
| 695 |
-
//
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 702 |
}
|
| 703 |
|
| 704 |
/// Well-known byte offsets into DDGame, for use with raw pointer access.
|
|
|
|
| 369 |
pub fn init_constructor_addrs() {
|
| 370 |
unsafe {
|
| 371 |
INIT_FIELDS_ADDR = rb(va::DDGAME_INIT_FIELDS);
|
| 372 |
+
SPRITE_REGION_CTOR_ADDR = rb(va::SPRITE_REGION_CONSTRUCTOR);
|
| 373 |
}
|
| 374 |
}
|
| 375 |
|
|
|
|
| 693 |
}
|
| 694 |
}
|
| 695 |
|
| 696 |
+
// ββ GfxResource (used by PCLandscape and later by gradients) ββ
|
| 697 |
+
let gfx_resource_create: unsafe extern "thiscall" fn(*mut u8, *mut u8) -> *mut u8 =
|
| 698 |
+
core::mem::transmute(rb(va::GFX_RESOURCE_CREATE) as usize);
|
| 699 |
+
let gfx_resource = gfx_resource_create(
|
| 700 |
+
(*wrapper)._field_4c0,
|
| 701 |
+
(ddgame as *mut u8).add(0x77A4), // output: pass ddgame+0x77A4 area
|
| 702 |
+
);
|
| 703 |
+
|
| 704 |
+
// ββ PCLandscape (alloc 0xB44, stdcall 11 params) ββ
|
| 705 |
+
let landscape = {
|
| 706 |
+
let alloc = wa_malloc(0xB44);
|
| 707 |
+
core::ptr::write_bytes(alloc, 0, 0xB44);
|
| 708 |
+
if !alloc.is_null() {
|
| 709 |
+
// Output locals for landscape data (used later for coord_list)
|
| 710 |
+
let mut landscape_coords: [i32; 2] = [0; 2]; // aiStack_11d4
|
| 711 |
+
let mut landscape_byte: u8 = 0; // iStack_11f9
|
| 712 |
+
|
| 713 |
+
let pc_ctor: unsafe extern "stdcall" fn(
|
| 714 |
+
*mut u8, *mut DDGame, *mut u8, // this, ddgame, gfx_resource
|
| 715 |
+
*mut DDGameWrapper, *const u8, // wrapper, game_info+0xDAAC
|
| 716 |
+
*mut u8, u32, // &landscape_byte, gfx_mode
|
| 717 |
+
*mut [i32; 2], *mut i32, // outputs: coords, more coords
|
| 718 |
+
*mut u8, *mut u8, // &ddgame+0x777C, &ddgame+0x7780
|
| 719 |
+
) -> *mut u8 = core::mem::transmute(rb(va::PC_LANDSCAPE_CONSTRUCTOR) as usize);
|
| 720 |
+
|
| 721 |
+
let gi = (*ddgame).game_info as *const u8;
|
| 722 |
+
let result = pc_ctor(
|
| 723 |
+
alloc,
|
| 724 |
+
ddgame,
|
| 725 |
+
gfx_resource,
|
| 726 |
+
wrapper,
|
| 727 |
+
gi.add(0xDAAC),
|
| 728 |
+
&mut landscape_byte as *mut u8,
|
| 729 |
+
(*wrapper).gfx_mode,
|
| 730 |
+
&mut landscape_coords as *mut [i32; 2],
|
| 731 |
+
landscape_coords.as_mut_ptr(), // placeholder for extra output
|
| 732 |
+
(ddgame as *mut u8).add(0x777C),
|
| 733 |
+
(ddgame as *mut u8).add(0x7780),
|
| 734 |
+
);
|
| 735 |
+
// Store landscape pointer
|
| 736 |
+
(*wrapper).landscape = result as *mut PCLandscape;
|
| 737 |
+
(*ddgame).landscape = result as *mut PCLandscape;
|
| 738 |
+
result
|
| 739 |
+
} else {
|
| 740 |
+
(*wrapper).landscape = core::ptr::null_mut();
|
| 741 |
+
(*ddgame).landscape = core::ptr::null_mut();
|
| 742 |
+
core::ptr::null_mut()
|
| 743 |
+
}
|
| 744 |
+
};
|
| 745 |
+
|
| 746 |
+
// ββ TaskStateMachine at DDGame+0x380 (alloc 0x2C) ββ
|
| 747 |
+
{
|
| 748 |
+
let tsm = wa_malloc(0x2C);
|
| 749 |
+
core::ptr::write_bytes(tsm, 0, 0x2C);
|
| 750 |
+
if !tsm.is_null() {
|
| 751 |
+
// TaskStateMachine__Init_Maybe is fastcall(ECX=width, EDX=?)
|
| 752 |
+
// but called here with ECX=1, then set vtable
|
| 753 |
+
let width = *((ddgame as *const u8).add(0x77C0) as *const u32);
|
| 754 |
+
let _height = *((ddgame as *const u8).add(0x77C4) as *const u32);
|
| 755 |
+
let tsm_init: unsafe extern "fastcall" fn(u32, u32) =
|
| 756 |
+
core::mem::transmute(rb(va::TASK_STATE_MACHINE_INIT) as usize);
|
| 757 |
+
tsm_init(1, width); // ECX=1, EDX=width
|
| 758 |
+
*(tsm as *mut u32) = rb(0x664118); // TaskStateMachine__vtable
|
| 759 |
+
}
|
| 760 |
+
let ddgame_ptr = ddgame as *mut u8;
|
| 761 |
+
*(ddgame_ptr.add(0x380) as *mut *mut u8) = tsm;
|
| 762 |
+
}
|
| 763 |
+
|
| 764 |
+
// ββ 8Γ SpriteRegion at DDGame+0x46C..0x488 ββ
|
| 765 |
+
// SpriteRegion__Constructor: fastcall(ECX, EDX) + 6 stack(this, p2, p3, p4, p5, p6), RET 0x18
|
| 766 |
+
{
|
| 767 |
+
// (ddgame_offset, ECX, EDX, stack2, stack3, stack4, stack5, stack6)
|
| 768 |
+
let regions: [(u32, u32, u32, u32, u32, u32, u32, u32); 8] = [
|
| 769 |
+
(0x474, 0x37, 0x36, 0x2E, 0x24, 0x41, 0x2D, 0),
|
| 770 |
+
(0x46C, 0x30, 0x0C, 0x2D, 0x07, 0x34, 0x09, 0),
|
| 771 |
+
(0x470, 0x11, 0x1A, 0x0D, 0x0A, 0x16, 0x13, 0),
|
| 772 |
+
(0x478, 0x21, 0x01, 0x00, 0x20, 0x22, 0x01, 0),
|
| 773 |
+
(0x47C, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0),
|
| 774 |
+
(0x484, 0x0A, 0x01, 0x173, 0x09, 0x17C, 0x02, 0),
|
| 775 |
+
(0x488, 0x08, 0x01, 0x1E5, 0x07, 0x1EC, 0x02, 0),
|
| 776 |
+
(0x480, 0x08, 0x01, 0x2D, 0x07, 0x34, 0x02, 0),
|
| 777 |
+
];
|
| 778 |
+
|
| 779 |
+
for &(offset, ecx, edx, p2, p3, p4, p5, p6) in ®ions {
|
| 780 |
+
let alloc = wa_malloc(0x9C);
|
| 781 |
+
core::ptr::write_bytes(alloc, 0, 0x9C);
|
| 782 |
+
let result = if !alloc.is_null() {
|
| 783 |
+
call_sprite_region_ctor(alloc, ecx, edx, p2, p3, p4, p5, p6)
|
| 784 |
+
} else {
|
| 785 |
+
core::ptr::null_mut()
|
| 786 |
+
};
|
| 787 |
+
*((ddgame as *mut u8).add(offset as usize) as *mut *mut u8) = result;
|
| 788 |
+
}
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
// ββ Landscape-derived value at DDGame+0x468 ββ
|
| 792 |
+
if !landscape.is_null() {
|
| 793 |
+
let land_vt = *(landscape as *const *const u32);
|
| 794 |
+
let get_val: unsafe extern "thiscall" fn(*mut u8) -> u32 =
|
| 795 |
+
core::mem::transmute(*land_vt.add(0xB));
|
| 796 |
+
*((ddgame as *mut u8).add(0x468) as *mut u32) = get_val(landscape);
|
| 797 |
+
}
|
| 798 |
+
|
| 799 |
+
// Release gfx_resource if non-null
|
| 800 |
+
if !gfx_resource.is_null() {
|
| 801 |
+
let rvt = *(gfx_resource as *const *const u32);
|
| 802 |
+
let release: unsafe extern "thiscall" fn(*mut u8) =
|
| 803 |
+
core::mem::transmute(*rvt.add(3));
|
| 804 |
+
release(gfx_resource);
|
| 805 |
+
}
|
| 806 |
+
|
| 807 |
+
// ββ Arrow sprites + collision regions (32 iterations) ββ
|
| 808 |
+
// ββ DisplayGfx at +0x138 ββ
|
| 809 |
+
// ββ CoordList at +0x50C ββ
|
| 810 |
+
// ββ Weapon sprites, gradient images, fill image, HUD ββ
|
| 811 |
+
// ββ Display finalization ββ
|
| 812 |
+
//
|
| 813 |
+
// These remaining sections (~300 lines of decompile) involve complex
|
| 814 |
+
// GfxDir lookups, sprite loading, gradient computation, and display
|
| 815 |
+
// layer management. They will be added in subsequent commits.
|
| 816 |
+
// For now, the constructor returns after audio + landscape + sprite regions.
|
| 817 |
+
}
|
| 818 |
+
|
| 819 |
+
/// Bridge to SpriteRegion__Constructor (0x57DB20).
|
| 820 |
+
/// Convention: fastcall(ECX, EDX) + 6 stack params, RET 0x18.
|
| 821 |
+
#[cfg(target_arch = "x86")]
|
| 822 |
+
static mut SPRITE_REGION_CTOR_ADDR: u32 = 0;
|
| 823 |
+
|
| 824 |
+
#[cfg(target_arch = "x86")]
|
| 825 |
+
#[unsafe(naked)]
|
| 826 |
+
unsafe extern "C" fn call_sprite_region_ctor(
|
| 827 |
+
_this: *mut u8, _ecx: u32, _edx: u32,
|
| 828 |
+
_p2: u32, _p3: u32, _p4: u32, _p5: u32, _p6: u32,
|
| 829 |
+
) -> *mut u8 {
|
| 830 |
+
// Stack on entry: [ret] [this] [ecx] [edx] [p2] [p3] [p4] [p5] [p6]
|
| 831 |
+
// Need: ECX=ecx, EDX=edx, push p6 p5 p4 p3 p2 this, call
|
| 832 |
+
core::arch::naked_asm!(
|
| 833 |
+
"pushl %ebx",
|
| 834 |
+
// Load ECX and EDX from our params
|
| 835 |
+
"movl 12(%esp), %ecx", // ecx param
|
| 836 |
+
"movl 16(%esp), %edx", // edx param
|
| 837 |
+
// Push stack params in reverse: p6, p5, p4, p3, p2, this
|
| 838 |
+
"movl 36(%esp), %eax", // p6
|
| 839 |
+
"pushl %eax",
|
| 840 |
+
"movl 36(%esp), %eax", // p5 (shifted by 4)
|
| 841 |
+
"pushl %eax",
|
| 842 |
+
"movl 36(%esp), %eax", // p4 (shifted by 8)
|
| 843 |
+
"pushl %eax",
|
| 844 |
+
"movl 36(%esp), %eax", // p3 (shifted by 12)
|
| 845 |
+
"pushl %eax",
|
| 846 |
+
"movl 36(%esp), %eax", // p2 (shifted by 16)
|
| 847 |
+
"pushl %eax",
|
| 848 |
+
"movl 28(%esp), %eax", // this (shifted by 20)
|
| 849 |
+
"pushl %eax",
|
| 850 |
+
"calll *({addr})", // fastcall, callee cleans 6Γ4=24 bytes
|
| 851 |
+
"popl %ebx",
|
| 852 |
+
"retl",
|
| 853 |
+
addr = sym SPRITE_REGION_CTOR_ADDR,
|
| 854 |
+
options(att_syntax),
|
| 855 |
+
);
|
| 856 |
}
|
| 857 |
|
| 858 |
/// Well-known byte offsets into DDGame, for use with raw pointer access.
|
crates/openwa-wormkit/src/replacements/game_session.rs
CHANGED
|
@@ -9,16 +9,16 @@
|
|
| 9 |
//!
|
| 10 |
//! - `DDGameWrapper__InitReplay` (0x56F860): usercall(EAX=game_info, ESI=this),
|
| 11 |
//! plain RET (no stack args). Bridged via `call_init_replay`.
|
| 12 |
-
//! - `DDGame__Constructor` (0x56E220):
|
| 13 |
-
//!
|
|
|
|
| 14 |
//! - `DDGame__InitGameState` (0x526500): plain stdcall(this), called via transmute.
|
| 15 |
|
| 16 |
use openwa_core::address::va;
|
| 17 |
use openwa_core::audio::DSSound;
|
| 18 |
use openwa_core::display::{DDDisplay, Palette};
|
| 19 |
-
use openwa_core::engine::ddgame::
|
| 20 |
use openwa_core::engine::{DDGameWrapper, GameInfo, GameSession};
|
| 21 |
-
use openwa_core::input::DDKeyboard;
|
| 22 |
use openwa_core::rebase::rb;
|
| 23 |
use crate::hook;
|
| 24 |
use crate::log_line;
|
|
@@ -26,9 +26,16 @@ use crate::log_line;
|
|
| 26 |
/// Implicit EDI = game_info pointer, captured from EDI on entry.
|
| 27 |
static mut GAME_INFO: *mut GameInfo = core::ptr::null_mut();
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
/// Runtime address of `DDGameWrapper__InitReplay` (set at install time).
|
| 30 |
static mut INIT_REPLAY_ADDR: u32 = 0;
|
| 31 |
|
|
|
|
|
|
|
|
|
|
| 32 |
// βββ Bridge: DDGameWrapper__InitReplay βββββββββββββββββββββββββββββββββββββββ
|
| 33 |
//
|
| 34 |
// Convention: usercall(EAX=game_info, ESI=this), plain RET (no stack params).
|
|
@@ -46,6 +53,31 @@ unsafe extern "stdcall" fn call_init_replay(_game_info: *mut GameInfo, _this: *m
|
|
| 46 |
);
|
| 47 |
}
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
/// Called by `impl_init_hardware` to construct the DDGameWrapper in-place.
|
| 50 |
pub(crate) unsafe fn construct_ddgame_wrapper(
|
| 51 |
game_info: *mut GameInfo,
|
|
@@ -74,18 +106,12 @@ pub(crate) unsafe fn construct_ddgame_wrapper(
|
|
| 74 |
let timer_obj = (*session).timer_obj;
|
| 75 |
let net_game = (*session).net_game;
|
| 76 |
|
| 77 |
-
//
|
| 78 |
-
create_ddgame(
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
display,
|
| 82 |
-
|
| 83 |
-
palette,
|
| 84 |
-
streaming_audio as *mut openwa_core::audio::Music,
|
| 85 |
-
timer_obj,
|
| 86 |
-
net_game,
|
| 87 |
-
game_info,
|
| 88 |
-
input_ctrl as u32, // network ECX
|
| 89 |
);
|
| 90 |
|
| 91 |
// Initialize DDGame's game-state fields.
|
|
@@ -105,11 +131,13 @@ pub(crate) unsafe fn construct_ddgame_wrapper(
|
|
| 105 |
pub fn install() -> Result<(), String> {
|
| 106 |
unsafe {
|
| 107 |
INIT_REPLAY_ADDR = rb(va::DDGAMEWRAPPER_INIT_REPLAY);
|
| 108 |
-
|
|
|
|
| 109 |
init_constructor_addrs();
|
| 110 |
-
//
|
| 111 |
hook::install_trap!("DDGameWrapper__Constructor", va::CONSTRUCT_DD_GAME_WRAPPER);
|
| 112 |
-
|
|
|
|
| 113 |
}
|
| 114 |
Ok(())
|
| 115 |
}
|
|
|
|
| 9 |
//!
|
| 10 |
//! - `DDGameWrapper__InitReplay` (0x56F860): usercall(EAX=game_info, ESI=this),
|
| 11 |
//! plain RET (no stack args). Bridged via `call_init_replay`.
|
| 12 |
+
//! - `DDGame__Constructor` (0x56E220): stdcall 9 params + implicit ECX=network.
|
| 13 |
+
//! Bridged via `ddgame_constructor_call`. Being incrementally replaced by
|
| 14 |
+
//! `create_ddgame()` in openwa-core (not yet complete).
|
| 15 |
//! - `DDGame__InitGameState` (0x526500): plain stdcall(this), called via transmute.
|
| 16 |
|
| 17 |
use openwa_core::address::va;
|
| 18 |
use openwa_core::audio::DSSound;
|
| 19 |
use openwa_core::display::{DDDisplay, Palette};
|
| 20 |
+
use openwa_core::engine::ddgame::init_constructor_addrs;
|
| 21 |
use openwa_core::engine::{DDGameWrapper, GameInfo, GameSession};
|
|
|
|
| 22 |
use openwa_core::rebase::rb;
|
| 23 |
use crate::hook;
|
| 24 |
use crate::log_line;
|
|
|
|
| 26 |
/// Implicit EDI = game_info pointer, captured from EDI on entry.
|
| 27 |
static mut GAME_INFO: *mut GameInfo = core::ptr::null_mut();
|
| 28 |
|
| 29 |
+
/// Implicit ECX = network pointer for `DDGame__Constructor`. Set in `ctor_impl`
|
| 30 |
+
/// just before calling `ddgame_constructor_call`.
|
| 31 |
+
static mut DDGAME_CTOR_ECX: u32 = 0;
|
| 32 |
+
|
| 33 |
/// Runtime address of `DDGameWrapper__InitReplay` (set at install time).
|
| 34 |
static mut INIT_REPLAY_ADDR: u32 = 0;
|
| 35 |
|
| 36 |
+
/// Runtime address of `DDGame__Constructor` (set at install time).
|
| 37 |
+
static mut DDGAME_CTOR_ADDR: u32 = 0;
|
| 38 |
+
|
| 39 |
// βββ Bridge: DDGameWrapper__InitReplay βββββββββββββββββββββββββββββββββββββββ
|
| 40 |
//
|
| 41 |
// Convention: usercall(EAX=game_info, ESI=this), plain RET (no stack params).
|
|
|
|
| 53 |
);
|
| 54 |
}
|
| 55 |
|
| 56 |
+
// βββ Bridge: DDGame__Constructor βββββββββββββββββββββββββββββββββββββββββββββ
|
| 57 |
+
//
|
| 58 |
+
// Convention: stdcall 9 params + implicit ECX=network.
|
| 59 |
+
// Tail-jumps to original; DDGame::ctor's RET 0x24 returns to caller.
|
| 60 |
+
#[unsafe(naked)]
|
| 61 |
+
unsafe extern "stdcall" fn ddgame_constructor_call(
|
| 62 |
+
_this: *mut DDGameWrapper,
|
| 63 |
+
_display: *mut DDDisplay,
|
| 64 |
+
_sound: *mut DSSound,
|
| 65 |
+
_keyboard: *mut u8,
|
| 66 |
+
_palette: *mut Palette,
|
| 67 |
+
_streaming_audio: *mut u8,
|
| 68 |
+
_timer_obj: *mut u8,
|
| 69 |
+
_net_game: *mut u8,
|
| 70 |
+
_game_info: *mut GameInfo,
|
| 71 |
+
) -> *mut u8 {
|
| 72 |
+
core::arch::naked_asm!(
|
| 73 |
+
"movl {ecx_val}, %ecx", // ECX = network (implicit param)
|
| 74 |
+
"jmpl *({fn})", // tail-jump; RET 0x24 returns to caller
|
| 75 |
+
ecx_val = sym DDGAME_CTOR_ECX,
|
| 76 |
+
fn = sym DDGAME_CTOR_ADDR,
|
| 77 |
+
options(att_syntax),
|
| 78 |
+
);
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
/// Called by `impl_init_hardware` to construct the DDGameWrapper in-place.
|
| 82 |
pub(crate) unsafe fn construct_ddgame_wrapper(
|
| 83 |
game_info: *mut GameInfo,
|
|
|
|
| 106 |
let timer_obj = (*session).timer_obj;
|
| 107 |
let net_game = (*session).net_game;
|
| 108 |
|
| 109 |
+
// Call original DDGame__Constructor via bridge.
|
| 110 |
+
// TODO: Replace with create_ddgame() once fully ported.
|
| 111 |
+
DDGAME_CTOR_ECX = input_ctrl as u32;
|
| 112 |
+
ddgame_constructor_call(
|
| 113 |
+
this, display, sound, keyboard, palette, streaming_audio,
|
| 114 |
+
timer_obj, net_game, game_info,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
);
|
| 116 |
|
| 117 |
// Initialize DDGame's game-state fields.
|
|
|
|
| 131 |
pub fn install() -> Result<(), String> {
|
| 132 |
unsafe {
|
| 133 |
INIT_REPLAY_ADDR = rb(va::DDGAMEWRAPPER_INIT_REPLAY);
|
| 134 |
+
DDGAME_CTOR_ADDR = rb(va::CONSTRUCT_DD_GAME);
|
| 135 |
+
// Initialize runtime addresses for create_ddgame bridges (future use).
|
| 136 |
init_constructor_addrs();
|
| 137 |
+
// DDGameWrapper__Constructor is fully converted β trap the original.
|
| 138 |
hook::install_trap!("DDGameWrapper__Constructor", va::CONSTRUCT_DD_GAME_WRAPPER);
|
| 139 |
+
// DDGame__Constructor still uses the original via bridge.
|
| 140 |
+
// Will be trapped once create_ddgame() is complete.
|
| 141 |
}
|
| 142 |
Ok(())
|
| 143 |
}
|