paavohtl Claude Opus 4.6 (1M context) commited on
Commit
aae5253
Β·
1 Parent(s): c7109bc

feat: port SpriteGfxTable__Init + RingBuffer__Init to Rust (Group A)

Browse files

First 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
+ }