refactor: add wa_malloc_zeroed, use typed arrays and &raw mut in DDGame ctor
Browse filesAdd wa_malloc_zeroed() utility that combines wa_malloc + write_bytes.
Replace 10 malloc+zero pairs in DDGame constructor with single calls.
Type landscape temp buffers as [u32] arrays instead of [u8], enabling
direct indexing for coordinate data. Update wa_pc_landscape_ctor
signature to match (*mut u32 for output params). Use &raw mut for
obtaining raw pointers to DDGame fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -19,7 +19,7 @@ use crate::render::landscape::PCLandscape;
|
|
| 19 |
use crate::render::queue::RenderQueue;
|
| 20 |
use crate::render::turn_order::TurnOrderWidget;
|
| 21 |
use crate::task::bit_grid::BitGrid;
|
| 22 |
-
use crate::wa_alloc::wa_malloc;
|
| 23 |
|
| 24 |
/// DDGame β the main game engine object.
|
| 25 |
///
|
|
@@ -586,10 +586,10 @@ unsafe fn wa_pc_landscape_ctor(
|
|
| 586 |
landscape_path: *const u8,
|
| 587 |
byte_output: *mut u8,
|
| 588 |
gfx_mode: u32,
|
| 589 |
-
temp_buf: *mut
|
| 590 |
-
coord_output: *mut
|
| 591 |
-
width_output: *mut
|
| 592 |
-
height_output: *mut
|
| 593 |
) -> *mut u8 {
|
| 594 |
let f: unsafe extern "stdcall" fn(
|
| 595 |
*mut u8,
|
|
@@ -599,10 +599,10 @@ unsafe fn wa_pc_landscape_ctor(
|
|
| 599 |
*const u8,
|
| 600 |
*mut u8,
|
| 601 |
u32,
|
| 602 |
-
*mut
|
| 603 |
-
*mut
|
| 604 |
-
*mut
|
| 605 |
-
*mut
|
| 606 |
) -> *mut u8 = core::mem::transmute(rb(va::PC_LANDSCAPE_CONSTRUCTOR) as usize);
|
| 607 |
f(
|
| 608 |
this,
|
|
@@ -680,11 +680,10 @@ pub unsafe fn create_ddgame(
|
|
| 680 |
network_ecx: u32, // implicit ECX from caller
|
| 681 |
) -> *mut DDGame {
|
| 682 |
// ββ 1. Allocate and zero-fill (matches: memset(piVar3, 0, 0x98B8)) ββ
|
| 683 |
-
let ddgame =
|
| 684 |
if ddgame.is_null() {
|
| 685 |
return core::ptr::null_mut();
|
| 686 |
}
|
| 687 |
-
core::ptr::write_bytes(ddgame as *mut u8, 0, 0x98B8);
|
| 688 |
|
| 689 |
// Notify watchpoint debugger (if active) so it can arm DR0βDR3.
|
| 690 |
if let Some(cb) = ON_DDGAME_ALLOC {
|
|
@@ -725,8 +724,7 @@ pub unsafe fn create_ddgame(
|
|
| 725 |
|
| 726 |
// ββ 8. Conditional network object (game_version == -2) ββ
|
| 727 |
if (*game_info).game_version == -2 {
|
| 728 |
-
let net_obj =
|
| 729 |
-
core::ptr::write_bytes(net_obj, 0, 0x2C);
|
| 730 |
*(net_obj as *mut *mut DDGame) = ddgame;
|
| 731 |
*net_obj.add(0x28) = (*game_info).net_config_1;
|
| 732 |
*net_obj.add(0x29) = (*game_info).net_config_2;
|
|
@@ -918,8 +916,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 918 |
|
| 919 |
// ββ Secondary GfxDir object (DDGame+0x2C, conditional) ββ
|
| 920 |
if !(*wrapper).secondary_gfx_dir.is_null() {
|
| 921 |
-
let gfxdir2 =
|
| 922 |
-
core::ptr::write_bytes(gfxdir2, 0, 0x70C);
|
| 923 |
*(gfxdir2 as *mut u16) = 1;
|
| 924 |
*(gfxdir2.add(2) as *mut u16) = 0x5A;
|
| 925 |
// FUN_5411A0: usercall(EAX=gfxdir2), plain RET
|
|
@@ -967,8 +964,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 967 |
let gfx_resource: *mut u8;
|
| 968 |
{
|
| 969 |
let gfx_dir = (*wrapper).primary_gfx_dir;
|
| 970 |
-
let out_buf =
|
| 971 |
-
core::ptr::write_bytes(out_buf, 0, 0x900);
|
| 972 |
gfx_resource =
|
| 973 |
gfx_resource_create(gfx_dir, rb(va::STR_MASKS_IMG) as *const c_char, out_buf);
|
| 974 |
}
|
|
@@ -976,13 +972,12 @@ unsafe fn init_graphics_and_resources(
|
|
| 976 |
// ββ PCLandscape (alloc 0xB44, stdcall 11 params) ββ
|
| 977 |
// Temporary output buffers for landscape coordinate data (used later for coord_list).
|
| 978 |
// These were stack locals in the original code (aiStack_978, iStack_11f9).
|
| 979 |
-
let mut landscape_coords_buf = [
|
| 980 |
let mut landscape_byte_buf = [0u8; 0x100]; // byte output
|
| 981 |
-
let mut landscape_temp = [
|
| 982 |
|
| 983 |
let landscape = {
|
| 984 |
-
let alloc =
|
| 985 |
-
core::ptr::write_bytes(alloc, 0, 0xB44);
|
| 986 |
if !alloc.is_null() {
|
| 987 |
let result = wa_pc_landscape_ctor(
|
| 988 |
alloc,
|
|
@@ -994,8 +989,8 @@ unsafe fn init_graphics_and_resources(
|
|
| 994 |
(*wrapper).gfx_mode,
|
| 995 |
landscape_temp.as_mut_ptr(),
|
| 996 |
landscape_coords_buf.as_mut_ptr(),
|
| 997 |
-
&mut (*ddgame).level_width_raw
|
| 998 |
-
&mut (*ddgame).level_height_raw
|
| 999 |
);
|
| 1000 |
(*wrapper).landscape = result as *mut PCLandscape;
|
| 1001 |
(*ddgame).landscape = result as *mut PCLandscape;
|
|
@@ -1039,8 +1034,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1039 |
];
|
| 1040 |
|
| 1041 |
for &(offset, ecx, edx, p2, p3, p4, p5, p6) in ®ions {
|
| 1042 |
-
let alloc =
|
| 1043 |
-
core::ptr::write_bytes(alloc, 0, 0x9C);
|
| 1044 |
let result = if !alloc.is_null() {
|
| 1045 |
call_sprite_region_ctor(alloc, ecx, edx, p2, p3, p4, p5, p6)
|
| 1046 |
} else {
|
|
@@ -1106,8 +1100,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1106 |
let half_h = (sprite_h / 2 - 10).max(0);
|
| 1107 |
|
| 1108 |
// Create SpriteRegion for collision
|
| 1109 |
-
let alloc =
|
| 1110 |
-
core::ptr::write_bytes(alloc, 0, 0x9C);
|
| 1111 |
let region = if !alloc.is_null() {
|
| 1112 |
// SpriteRegion params: ECX, EDX, this, p2, p3, p4, p5, p6
|
| 1113 |
// this[3] = p4 - p2 (width), this[4] = EDX - p3 (height)
|
|
@@ -1162,8 +1155,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1162 |
let cl = wa_malloc(12) as *mut u32;
|
| 1163 |
*cl = 0; // count
|
| 1164 |
*cl.add(1) = 600; // capacity
|
| 1165 |
-
let data =
|
| 1166 |
-
core::ptr::write_bytes(data, 0, 0x12C0);
|
| 1167 |
*cl.add(2) = data as u32;
|
| 1168 |
(*ddgame).coord_list = cl as *mut u8;
|
| 1169 |
|
|
@@ -1171,12 +1163,11 @@ unsafe fn init_graphics_and_resources(
|
|
| 1171 |
// landscape_temp[0] = coordinate count, landscape_coords_buf = pairs of (x, y).
|
| 1172 |
// Original packs as: coord = x * 0x10000 + y (Fixed-point).
|
| 1173 |
// Each coord_list entry is 8 bytes: [coord_value, 1]. Duplicates are skipped.
|
| 1174 |
-
let coord_count =
|
| 1175 |
-
let coords_src = landscape_coords_buf.as_ptr() as *const u32;
|
| 1176 |
let data_ptr = data as *mut u32;
|
| 1177 |
for j in 0..coord_count as usize {
|
| 1178 |
-
let x =
|
| 1179 |
-
let y =
|
| 1180 |
let coord_val = x.wrapping_mul(0x10000).wrapping_add(y);
|
| 1181 |
let cur_count = *cl as usize;
|
| 1182 |
if cur_count >= 600 {
|
|
@@ -1325,9 +1316,8 @@ unsafe fn init_graphics_and_resources(
|
|
| 1325 |
// ββ Gradient image stub (DDGame+0x30) ββ
|
| 1326 |
// Minimal stub: [6]=0 (zero-width) so CTaskLand skips the gradient column loop.
|
| 1327 |
if (*ddgame).gradient_image.is_null() {
|
| 1328 |
-
let obj =
|
| 1329 |
if !obj.is_null() {
|
| 1330 |
-
core::ptr::write_bytes(obj as *mut u8, 0, core::mem::size_of::<BitGrid>());
|
| 1331 |
(*obj).vtable = rb(va::BIT_GRID_VTABLE);
|
| 1332 |
// height = 0 β CTaskLand skips the gradient column loop
|
| 1333 |
(*ddgame).gradient_image = obj as *mut u8;
|
|
|
|
| 19 |
use crate::render::queue::RenderQueue;
|
| 20 |
use crate::render::turn_order::TurnOrderWidget;
|
| 21 |
use crate::task::bit_grid::BitGrid;
|
| 22 |
+
use crate::wa_alloc::{wa_malloc, wa_malloc_zeroed};
|
| 23 |
|
| 24 |
/// DDGame β the main game engine object.
|
| 25 |
///
|
|
|
|
| 586 |
landscape_path: *const u8,
|
| 587 |
byte_output: *mut u8,
|
| 588 |
gfx_mode: u32,
|
| 589 |
+
temp_buf: *mut u32,
|
| 590 |
+
coord_output: *mut u32,
|
| 591 |
+
width_output: *mut u32,
|
| 592 |
+
height_output: *mut u32,
|
| 593 |
) -> *mut u8 {
|
| 594 |
let f: unsafe extern "stdcall" fn(
|
| 595 |
*mut u8,
|
|
|
|
| 599 |
*const u8,
|
| 600 |
*mut u8,
|
| 601 |
u32,
|
| 602 |
+
*mut u32,
|
| 603 |
+
*mut u32,
|
| 604 |
+
*mut u32,
|
| 605 |
+
*mut u32,
|
| 606 |
) -> *mut u8 = core::mem::transmute(rb(va::PC_LANDSCAPE_CONSTRUCTOR) as usize);
|
| 607 |
f(
|
| 608 |
this,
|
|
|
|
| 680 |
network_ecx: u32, // implicit ECX from caller
|
| 681 |
) -> *mut DDGame {
|
| 682 |
// ββ 1. Allocate and zero-fill (matches: memset(piVar3, 0, 0x98B8)) ββ
|
| 683 |
+
let ddgame = wa_malloc_zeroed(0x98B8) as *mut DDGame;
|
| 684 |
if ddgame.is_null() {
|
| 685 |
return core::ptr::null_mut();
|
| 686 |
}
|
|
|
|
| 687 |
|
| 688 |
// Notify watchpoint debugger (if active) so it can arm DR0βDR3.
|
| 689 |
if let Some(cb) = ON_DDGAME_ALLOC {
|
|
|
|
| 724 |
|
| 725 |
// ββ 8. Conditional network object (game_version == -2) ββ
|
| 726 |
if (*game_info).game_version == -2 {
|
| 727 |
+
let net_obj = wa_malloc_zeroed(0x2C);
|
|
|
|
| 728 |
*(net_obj as *mut *mut DDGame) = ddgame;
|
| 729 |
*net_obj.add(0x28) = (*game_info).net_config_1;
|
| 730 |
*net_obj.add(0x29) = (*game_info).net_config_2;
|
|
|
|
| 916 |
|
| 917 |
// ββ Secondary GfxDir object (DDGame+0x2C, conditional) ββ
|
| 918 |
if !(*wrapper).secondary_gfx_dir.is_null() {
|
| 919 |
+
let gfxdir2 = wa_malloc_zeroed(0x70C);
|
|
|
|
| 920 |
*(gfxdir2 as *mut u16) = 1;
|
| 921 |
*(gfxdir2.add(2) as *mut u16) = 0x5A;
|
| 922 |
// FUN_5411A0: usercall(EAX=gfxdir2), plain RET
|
|
|
|
| 964 |
let gfx_resource: *mut u8;
|
| 965 |
{
|
| 966 |
let gfx_dir = (*wrapper).primary_gfx_dir;
|
| 967 |
+
let out_buf = wa_malloc_zeroed(0x900);
|
|
|
|
| 968 |
gfx_resource =
|
| 969 |
gfx_resource_create(gfx_dir, rb(va::STR_MASKS_IMG) as *const c_char, out_buf);
|
| 970 |
}
|
|
|
|
| 972 |
// ββ PCLandscape (alloc 0xB44, stdcall 11 params) ββ
|
| 973 |
// Temporary output buffers for landscape coordinate data (used later for coord_list).
|
| 974 |
// These were stack locals in the original code (aiStack_978, iStack_11f9).
|
| 975 |
+
let mut landscape_coords_buf = [0u32; 0x400]; // coord output: pairs of (x, y)
|
| 976 |
let mut landscape_byte_buf = [0u8; 0x100]; // byte output
|
| 977 |
+
let mut landscape_temp = [0u32; 0x400]; // [0] = coord count, rest = temp data
|
| 978 |
|
| 979 |
let landscape = {
|
| 980 |
+
let alloc = wa_malloc_zeroed(0xB44);
|
|
|
|
| 981 |
if !alloc.is_null() {
|
| 982 |
let result = wa_pc_landscape_ctor(
|
| 983 |
alloc,
|
|
|
|
| 989 |
(*wrapper).gfx_mode,
|
| 990 |
landscape_temp.as_mut_ptr(),
|
| 991 |
landscape_coords_buf.as_mut_ptr(),
|
| 992 |
+
&raw mut (*ddgame).level_width_raw,
|
| 993 |
+
&raw mut (*ddgame).level_height_raw,
|
| 994 |
);
|
| 995 |
(*wrapper).landscape = result as *mut PCLandscape;
|
| 996 |
(*ddgame).landscape = result as *mut PCLandscape;
|
|
|
|
| 1034 |
];
|
| 1035 |
|
| 1036 |
for &(offset, ecx, edx, p2, p3, p4, p5, p6) in ®ions {
|
| 1037 |
+
let alloc = wa_malloc_zeroed(0x9C);
|
|
|
|
| 1038 |
let result = if !alloc.is_null() {
|
| 1039 |
call_sprite_region_ctor(alloc, ecx, edx, p2, p3, p4, p5, p6)
|
| 1040 |
} else {
|
|
|
|
| 1100 |
let half_h = (sprite_h / 2 - 10).max(0);
|
| 1101 |
|
| 1102 |
// Create SpriteRegion for collision
|
| 1103 |
+
let alloc = wa_malloc_zeroed(0x9C);
|
|
|
|
| 1104 |
let region = if !alloc.is_null() {
|
| 1105 |
// SpriteRegion params: ECX, EDX, this, p2, p3, p4, p5, p6
|
| 1106 |
// this[3] = p4 - p2 (width), this[4] = EDX - p3 (height)
|
|
|
|
| 1155 |
let cl = wa_malloc(12) as *mut u32;
|
| 1156 |
*cl = 0; // count
|
| 1157 |
*cl.add(1) = 600; // capacity
|
| 1158 |
+
let data = wa_malloc_zeroed(0x12C0);
|
|
|
|
| 1159 |
*cl.add(2) = data as u32;
|
| 1160 |
(*ddgame).coord_list = cl as *mut u8;
|
| 1161 |
|
|
|
|
| 1163 |
// landscape_temp[0] = coordinate count, landscape_coords_buf = pairs of (x, y).
|
| 1164 |
// Original packs as: coord = x * 0x10000 + y (Fixed-point).
|
| 1165 |
// Each coord_list entry is 8 bytes: [coord_value, 1]. Duplicates are skipped.
|
| 1166 |
+
let coord_count = landscape_temp[0];
|
|
|
|
| 1167 |
let data_ptr = data as *mut u32;
|
| 1168 |
for j in 0..coord_count as usize {
|
| 1169 |
+
let x = landscape_coords_buf[j * 2];
|
| 1170 |
+
let y = landscape_coords_buf[j * 2 + 1];
|
| 1171 |
let coord_val = x.wrapping_mul(0x10000).wrapping_add(y);
|
| 1172 |
let cur_count = *cl as usize;
|
| 1173 |
if cur_count >= 600 {
|
|
|
|
| 1316 |
// ββ Gradient image stub (DDGame+0x30) ββ
|
| 1317 |
// Minimal stub: [6]=0 (zero-width) so CTaskLand skips the gradient column loop.
|
| 1318 |
if (*ddgame).gradient_image.is_null() {
|
| 1319 |
+
let obj = wa_malloc_zeroed(core::mem::size_of::<BitGrid>() as u32) as *mut BitGrid;
|
| 1320 |
if !obj.is_null() {
|
|
|
|
| 1321 |
(*obj).vtable = rb(va::BIT_GRID_VTABLE);
|
| 1322 |
// height = 0 β CTaskLand skips the gradient column loop
|
| 1323 |
(*ddgame).gradient_image = obj as *mut u8;
|
crates/openwa-core/src/wa_alloc.rs
CHANGED
|
@@ -13,6 +13,20 @@ pub unsafe fn wa_malloc(size: u32) -> *mut u8 {
|
|
| 13 |
f(size)
|
| 14 |
}
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
/// Free a pointer allocated by [`wa_malloc`] (WA's statically-linked CRT `free`).
|
| 17 |
///
|
| 18 |
/// # Safety
|
|
|
|
| 13 |
f(size)
|
| 14 |
}
|
| 15 |
|
| 16 |
+
/// Allocate `size` bytes from WA's CRT heap and zero-initialize them.
|
| 17 |
+
///
|
| 18 |
+
/// Equivalent to `wa_malloc(size)` followed by `write_bytes(ptr, 0, size)`.
|
| 19 |
+
///
|
| 20 |
+
/// # Safety
|
| 21 |
+
/// Must only be called from within the WA.exe process.
|
| 22 |
+
pub unsafe fn wa_malloc_zeroed(size: u32) -> *mut u8 {
|
| 23 |
+
let ptr = wa_malloc(size);
|
| 24 |
+
if !ptr.is_null() {
|
| 25 |
+
core::ptr::write_bytes(ptr, 0, size as usize);
|
| 26 |
+
}
|
| 27 |
+
ptr
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
/// Free a pointer allocated by [`wa_malloc`] (WA's statically-linked CRT `free`).
|
| 31 |
///
|
| 32 |
/// # Safety
|