feat: port SpriteGfxTable__Init + RingBuffer__Init to Rust (Group A)
Browse filesFirst two InitGameState sub-functions ported as hooks:
- SpriteGfxTable__Init (0x541620): fastcall(ECX=base, EDX=count).
Initializes identity permutation + 0xFFFFFFFF lookup table.
15 lines, zero dependencies.
- RingBuffer__Init (0x541060): usercall(EAX=capacity, ESI=struct).
Allocates buffer, initializes 7-DWORD ring buffer struct.
15 lines, only calls wa_malloc.
New file: engine/game_state_init.rs for InitGameState sub-functions.
Headful + headless replay tests PASS.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/address.rs
CHANGED
|
@@ -350,6 +350,10 @@ pub mod va {
|
|
| 350 |
/// PaletteContext__Init (0x5411A0): initialize palette context free-list.
|
| 351 |
/// usercall(EAX=context), plain RET.
|
| 352 |
pub const PALETTE_CONTEXT_INIT: u32 = 0x0054_11A0;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
/// DDGame__InitDisplayFinal_Maybe (0x56A830): display finalization for non-headless.
|
| 354 |
pub const DDGAME_INIT_DISPLAY_FINAL: u32 = 0x0056_A830;
|
| 355 |
/// PCLandscape__Constructor (0x57ACB0): constructs 0xB44-byte landscape object.
|
|
|
|
| 350 |
/// PaletteContext__Init (0x5411A0): initialize palette context free-list.
|
| 351 |
/// usercall(EAX=context), plain RET.
|
| 352 |
pub const PALETTE_CONTEXT_INIT: u32 = 0x0054_11A0;
|
| 353 |
+
/// SpriteGfxTable__Init (0x541620): fastcall(ECX=base, EDX=count), plain RET.
|
| 354 |
+
pub const SPRITE_GFX_TABLE_INIT: u32 = 0x0054_1620;
|
| 355 |
+
/// RingBuffer__Init (0x541060): usercall(EAX=capacity, ESI=struct), plain RET.
|
| 356 |
+
pub const RING_BUFFER_INIT: u32 = 0x0054_1060;
|
| 357 |
/// DDGame__InitDisplayFinal_Maybe (0x56A830): display finalization for non-headless.
|
| 358 |
pub const DDGAME_INIT_DISPLAY_FINAL: u32 = 0x0056_A830;
|
| 359 |
/// PCLandscape__Constructor (0x57ACB0): constructs 0xB44-byte landscape object.
|
crates/openwa-core/src/engine/game_state_init.rs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! Pure Rust implementations of DDGame__InitGameState sub-functions.
|
| 2 |
+
//!
|
| 3 |
+
//! Each function is hooked individually so it works regardless of whether
|
| 4 |
+
//! InitGameState itself is Rust or the original WA code.
|
| 5 |
+
|
| 6 |
+
use crate::wa_alloc::wa_malloc;
|
| 7 |
+
|
| 8 |
+
/// Pure Rust implementation of SpriteGfxTable__Init (0x541620).
|
| 9 |
+
///
|
| 10 |
+
/// Convention: fastcall(ECX=base, EDX=count), plain RET.
|
| 11 |
+
///
|
| 12 |
+
/// Initializes two parallel arrays:
|
| 13 |
+
/// - `base[0..count]`: identity permutation (index[i] = i)
|
| 14 |
+
/// - `base+0x2000[0..count]`: all 0xFFFFFFFF (unused markers)
|
| 15 |
+
/// Plus 3 trailer fields at +0x3000/+0x3004/+0x3008.
|
| 16 |
+
pub unsafe fn sprite_gfx_table_init(base: *mut u8, count: u32) {
|
| 17 |
+
for i in 0..count {
|
| 18 |
+
// Index table: base + i*4 = i
|
| 19 |
+
*((base as *mut u32).add(i as usize)) = i;
|
| 20 |
+
// Lookup table: base + 0x2000 + i*4 = 0xFFFFFFFF
|
| 21 |
+
*((base.add(0x2000) as *mut u32).add(i as usize)) = 0xFFFF_FFFF;
|
| 22 |
+
}
|
| 23 |
+
*(base.add(0x3000) as *mut u32) = count;
|
| 24 |
+
*(base.add(0x3004) as *mut u32) = 0;
|
| 25 |
+
*(base.add(0x3008) as *mut u32) = count;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/// Pure Rust implementation of RingBuffer__Init (0x541060).
|
| 29 |
+
///
|
| 30 |
+
/// Convention: usercall(EAX=capacity, ESI=struct_ptr), plain RET.
|
| 31 |
+
///
|
| 32 |
+
/// Allocates a zero-filled buffer of `capacity` bytes (aligned + 0x20 header),
|
| 33 |
+
/// then initializes the ring buffer struct (7 DWORDs):
|
| 34 |
+
/// - [0]: data pointer
|
| 35 |
+
/// - [1]: capacity
|
| 36 |
+
/// - [2]-[6]: zeroed (head, tail, count, etc.)
|
| 37 |
+
pub unsafe fn ring_buffer_init(struct_ptr: *mut u8, capacity: u32) {
|
| 38 |
+
let alloc_size = ((capacity + 3) & !3) + 0x20;
|
| 39 |
+
let data = wa_malloc(alloc_size);
|
| 40 |
+
if !data.is_null() {
|
| 41 |
+
core::ptr::write_bytes(data, 0, capacity as usize);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
let s = struct_ptr as *mut u32;
|
| 45 |
+
*s.add(0) = data as u32; // data pointer
|
| 46 |
+
*s.add(1) = capacity; // capacity
|
| 47 |
+
*s.add(2) = 0; // field 2
|
| 48 |
+
*s.add(3) = 0; // field 3
|
| 49 |
+
*s.add(4) = 0; // field 4
|
| 50 |
+
*s.add(5) = 0; // field 5
|
| 51 |
+
*s.add(6) = 0; // field 6
|
| 52 |
+
}
|
crates/openwa-core/src/engine/mod.rs
CHANGED
|
@@ -2,6 +2,7 @@ pub mod ddgame;
|
|
| 2 |
pub mod ddgame_wrapper;
|
| 3 |
pub mod game_info;
|
| 4 |
pub mod game_session;
|
|
|
|
| 5 |
pub mod game_timer;
|
| 6 |
pub mod net_wrapper;
|
| 7 |
|
|
|
|
| 2 |
pub mod ddgame_wrapper;
|
| 3 |
pub mod game_info;
|
| 4 |
pub mod game_session;
|
| 5 |
+
pub mod game_state_init;
|
| 6 |
pub mod game_timer;
|
| 7 |
pub mod net_wrapper;
|
| 8 |
|
crates/openwa-wormkit/src/replacements/ddgame_init.rs
CHANGED
|
@@ -18,6 +18,7 @@ use openwa_core::engine::ddgame::{
|
|
| 18 |
bit_grid_init, ddgame_init_fields, ddgame_init_render_indices, display_layer_color_init,
|
| 19 |
gfx_dir_find_entry, gfx_dir_load_dir, gfx_resource_create, DDGame,
|
| 20 |
};
|
|
|
|
| 21 |
use openwa_core::engine::DDGameWrapper;
|
| 22 |
|
| 23 |
// βββ DDGame__InitFields (0x526120) ββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -200,7 +201,47 @@ pub fn install() -> Result<(), String> {
|
|
| 200 |
va::GFX_DIR_LOAD_DIR,
|
| 201 |
load_dir_trampoline as *const (),
|
| 202 |
)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
}
|
| 204 |
|
| 205 |
Ok(())
|
| 206 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
bit_grid_init, ddgame_init_fields, ddgame_init_render_indices, display_layer_color_init,
|
| 19 |
gfx_dir_find_entry, gfx_dir_load_dir, gfx_resource_create, DDGame,
|
| 20 |
};
|
| 21 |
+
use openwa_core::engine::game_state_init::{ring_buffer_init, sprite_gfx_table_init};
|
| 22 |
use openwa_core::engine::DDGameWrapper;
|
| 23 |
|
| 24 |
// βββ DDGame__InitFields (0x526120) ββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 201 |
va::GFX_DIR_LOAD_DIR,
|
| 202 |
load_dir_trampoline as *const (),
|
| 203 |
)?;
|
| 204 |
+
|
| 205 |
+
hook::install(
|
| 206 |
+
"SpriteGfxTable__Init",
|
| 207 |
+
va::SPRITE_GFX_TABLE_INIT,
|
| 208 |
+
sprite_gfx_table_init_trampoline as *const (),
|
| 209 |
+
)?;
|
| 210 |
+
|
| 211 |
+
hook::install(
|
| 212 |
+
"RingBuffer__Init",
|
| 213 |
+
va::RING_BUFFER_INIT,
|
| 214 |
+
ring_buffer_init_trampoline as *const (),
|
| 215 |
+
)?;
|
| 216 |
}
|
| 217 |
|
| 218 |
Ok(())
|
| 219 |
}
|
| 220 |
+
|
| 221 |
+
// βββ SpriteGfxTable__Init (0x541620) ββββββββββββββββββββββββββββββββββββββββ
|
| 222 |
+
// Convention: fastcall(ECX=base, EDX=count), plain RET.
|
| 223 |
+
|
| 224 |
+
unsafe extern "fastcall" fn sprite_gfx_table_init_trampoline(base: u32, count: u32) {
|
| 225 |
+
sprite_gfx_table_init(base as *mut u8, count);
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
// βββ RingBuffer__Init (0x541060) ββββββββββββββββββββββββββββββββββββββββββββ
|
| 229 |
+
// Convention: usercall(EAX=capacity, ESI=struct_ptr), plain RET.
|
| 230 |
+
|
| 231 |
+
extern "cdecl" fn impl_ring_buffer_init(struct_ptr: u32, capacity: u32) {
|
| 232 |
+
unsafe { ring_buffer_init(struct_ptr as *mut u8, capacity) }
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
#[unsafe(naked)]
|
| 236 |
+
unsafe extern "C" fn ring_buffer_init_trampoline() {
|
| 237 |
+
core::arch::naked_asm!(
|
| 238 |
+
"push edx",
|
| 239 |
+
"push eax", // capacity (EAX)
|
| 240 |
+
"push esi", // struct_ptr (ESI)
|
| 241 |
+
"call {impl_fn}",
|
| 242 |
+
"add esp, 8",
|
| 243 |
+
"pop edx",
|
| 244 |
+
"ret",
|
| 245 |
+
impl_fn = sym impl_ring_buffer_init,
|
| 246 |
+
);
|
| 247 |
+
}
|