refactor: clean up outdated comments in ddgame.rs
Browse files- Simplify verbose vtable calling convention comments to purpose descriptions
- Remove x86 instruction-level "Original:" comments (noise post-porting)
- Remove development breadcrumbs and debug provenance notes
- Condense multi-line comment blocks to concise summaries
- Remove empty else branch and obvious/redundant comments
- Keep TODO for unported complex gradient computation
- Improve doc comment on init_graphics_and_resources
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -446,7 +446,6 @@ pub unsafe fn ddgame_init_fields(ddgame: *mut DDGame) {
|
|
| 446 |
/// # Safety
|
| 447 |
/// `base` must point to a valid memory region with at least 0x4A4 bytes.
|
| 448 |
pub unsafe fn ddgame_init_render_indices(base: *mut u8) {
|
| 449 |
-
// Zero base+0xC4
|
| 450 |
*(base.add(0xC4) as *mut u32) = 0;
|
| 451 |
|
| 452 |
// eh_vector_constructor_iterator equivalent:
|
|
@@ -510,7 +509,6 @@ pub unsafe fn task_state_machine_init(object: *mut u8, param1: u32, width: u32,
|
|
| 510 |
core::ptr::write_bytes(buffer, 0, total_size as usize);
|
| 511 |
core::ptr::write_bytes(buffer, 0, total_size as usize);
|
| 512 |
|
| 513 |
-
// Set object fields (DWORD array layout)
|
| 514 |
let obj = object as *mut u32;
|
| 515 |
*obj.add(0) = rb(0x6640EC); // vtable
|
| 516 |
*obj.add(1) = 0; // unused
|
|
@@ -556,7 +554,7 @@ pub unsafe fn display_layer_color_init(wrapper: *mut DDGameWrapper) {
|
|
| 556 |
|
| 557 |
let display = (*wrapper).display as *mut u8;
|
| 558 |
let vt = *(display as *const *const u32);
|
| 559 |
-
// vtable[4]
|
| 560 |
let set_color: unsafe extern "thiscall" fn(*mut u8, i32, i32) =
|
| 561 |
core::mem::transmute(*vt.add(4));
|
| 562 |
|
|
@@ -821,7 +819,6 @@ pub unsafe fn gfx_handler_load_dir(handler: *mut u8) -> i32 {
|
|
| 821 |
}
|
| 822 |
}
|
| 823 |
|
| 824 |
-
// Mark as loaded
|
| 825 |
*(handler.add(0x194) as *mut u32) = 1;
|
| 826 |
|
| 827 |
1 // success
|
|
@@ -958,10 +955,7 @@ pub unsafe fn create_ddgame(
|
|
| 958 |
|
| 959 |
// ββ 6. Sound available + always-1 flags ββ
|
| 960 |
let is_headless = (*game_info).headless_mode != 0;
|
| 961 |
-
//
|
| 962 |
-
// This enables the loading progress bar, message pump, and sound during
|
| 963 |
-
// construction. Previously deferred to avoid message pump crashes, but all
|
| 964 |
-
// critical DDGame fields (game_info, display, sound, etc.) are now set.
|
| 965 |
(*ddgame).sound_available = if is_headless { 0 } else { 1 };
|
| 966 |
(*ddgame)._field_7efc = 1;
|
| 967 |
|
|
@@ -993,8 +987,7 @@ pub unsafe fn create_ddgame(
|
|
| 993 |
|
| 994 |
/// Second half of the constructor: GfxHandler, landscape, sprites, audio, resources.
|
| 995 |
///
|
| 996 |
-
///
|
| 997 |
-
/// individual sections are converted to pure Rust.
|
| 998 |
#[cfg(target_arch = "x86")]
|
| 999 |
unsafe fn init_graphics_and_resources(
|
| 1000 |
wrapper: *mut DDGameWrapper,
|
|
@@ -1006,7 +999,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1006 |
let ddgame = (*wrapper).ddgame;
|
| 1007 |
let fopen: unsafe extern "cdecl" fn(*const u8, *const u8) -> *mut u8 =
|
| 1008 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
| 1009 |
-
let gfx_handler_vtable = rb(0x66B280) as u32; //
|
| 1010 |
|
| 1011 |
// ββ GfxHandler #1 (primary) ββ
|
| 1012 |
let gfx1 = wa_malloc(0x19C);
|
|
@@ -1068,7 +1061,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1068 |
|
| 1069 |
// ββ GfxHandler #2 (conditional) ββ
|
| 1070 |
let game_version = (*game_info).game_version;
|
| 1071 |
-
// Original: (-(gfx_mode != 0) & 0x23) - 2 β gfx_mode ? 33 : -2
|
| 1072 |
let threshold = if (*wrapper).gfx_mode != 0 { 33 } else { -2i32 };
|
| 1073 |
if game_version < threshold {
|
| 1074 |
let c_digit = if game_version > -3 { b'2' } else { b'1' };
|
|
@@ -1096,21 +1088,19 @@ unsafe fn init_graphics_and_resources(
|
|
| 1096 |
if *(rb(0x88E485) as *const u8) == 0 {
|
| 1097 |
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
| 1098 |
}
|
| 1099 |
-
//
|
| 1100 |
-
// BUT: FUN_00570A90 or something else may write to wrapper in ways we
|
| 1101 |
-
// don't expect. Read via struct field instead of raw offset.
|
| 1102 |
let disp = (*wrapper).display as *mut u8;
|
| 1103 |
let gfx_handler = (*wrapper)._field_4c0;
|
| 1104 |
let vt = *(disp as *const *const u32);
|
| 1105 |
-
// vtable[4]
|
| 1106 |
let vt_10: unsafe extern "thiscall" fn(*mut u8, i32, i32) =
|
| 1107 |
core::mem::transmute(*vt.add(4));
|
| 1108 |
vt_10(disp, 1, 0xFE);
|
| 1109 |
-
// vtable[0x1F]
|
| 1110 |
let vt_7c: unsafe extern "thiscall" fn(*mut u8, i32, i32, i32, *mut u8, u32) =
|
| 1111 |
core::mem::transmute(*vt.add(0x1F));
|
| 1112 |
vt_7c(disp, 1, 1, 0, gfx_handler, rb(0x66A3A8));
|
| 1113 |
-
// vtable[0x17]
|
| 1114 |
let vt_5c: unsafe extern "thiscall" fn(*mut u8, i32, i32) =
|
| 1115 |
core::mem::transmute(*vt.add(0x17));
|
| 1116 |
vt_5c(disp, 1, -100);
|
|
@@ -1146,7 +1136,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1146 |
|
| 1147 |
// ββ GfxDir color entries DDGame+0x730C..0x732C ββ
|
| 1148 |
if (*wrapper).gfx_mode != 0 {
|
| 1149 |
-
// Original: PUSH ESI (=layer_ctx from vtable[5] call), MOV EAX=0x66A3B4, CALL 0x4F6300
|
| 1150 |
// The layer_ctx is used as the output buffer, not a plain stack alloc.
|
| 1151 |
let res = gfx_resource_create((*wrapper)._field_4c0, rb(0x66A3B4) as *const u8, layer_ctx);
|
| 1152 |
if !res.is_null() {
|
|
@@ -1168,8 +1157,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1168 |
core::mem::transmute(*rvt.add(3));
|
| 1169 |
release(res, 1);
|
| 1170 |
}
|
| 1171 |
-
} else {
|
| 1172 |
-
// gfx_mode == 0: no color entries to load
|
| 1173 |
}
|
| 1174 |
|
| 1175 |
// ββ Secondary GfxDir object (DDGame+0x2C, conditional) ββ
|
|
@@ -1196,9 +1183,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1196 |
|
| 1197 |
// ββ DDGameWrapper field inits ββ
|
| 1198 |
(*wrapper)._field_4d8 = 0;
|
| 1199 |
-
//
|
| 1200 |
-
// Ghidra mislabeled game_info as param8 β the original reads game_info, not net_game.
|
| 1201 |
-
// Our headless display is non-null, so use is_headless as the condition.
|
| 1202 |
if is_headless {
|
| 1203 |
(*wrapper)._field_4dc = 0x2AD;
|
| 1204 |
} else {
|
|
@@ -1278,7 +1263,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1278 |
(ddgame as *mut u8).add(0x777C), // param 10
|
| 1279 |
(ddgame as *mut u8).add(0x7780), // param 11
|
| 1280 |
);
|
| 1281 |
-
// Store landscape pointer
|
| 1282 |
(*wrapper).landscape = result as *mut PCLandscape;
|
| 1283 |
(*ddgame).landscape = result as *mut PCLandscape;
|
| 1284 |
result
|
|
@@ -1306,7 +1290,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1306 |
// ββ 8Γ SpriteRegion at DDGame+0x46C..0x488 ββ
|
| 1307 |
// SpriteRegion__Constructor: fastcall(ECX, EDX) + 6 stack(this, p2, p3, p4, p5, p6), RET 0x18
|
| 1308 |
{
|
| 1309 |
-
// Params verified by hooking original (see SpriteRegion param logger).
|
| 1310 |
// (ddgame_offset, ECX, EDX, p2, p3, p4, p5, p6=gfx_resource)
|
| 1311 |
// p6 is the GfxResource pointer returned by GfxResource__Create_Maybe.
|
| 1312 |
let gr = gfx_resource as u32;
|
|
@@ -1436,14 +1419,10 @@ unsafe fn init_graphics_and_resources(
|
|
| 1436 |
}
|
| 1437 |
|
| 1438 |
// ββ DisplayGfx at DDGame+0x138 ββ
|
| 1439 |
-
// Original: alloc 0x4C, memset 0x2C, PUSH 0x100, MOV ECX=8, MOV EDI=0x1E0,
|
| 1440 |
-
// CALL 0x4F6370, MOV [alloc]=0x664144
|
| 1441 |
{
|
| 1442 |
let tsm = wa_malloc(0x4C);
|
| 1443 |
core::ptr::write_bytes(tsm, 0, 0x2C);
|
| 1444 |
if !tsm.is_null() {
|
| 1445 |
-
// Original: TaskStateMachine__Init(ECX=8, EDI=0x1E0, stack=0x100)
|
| 1446 |
-
// β task_state_machine_init(object, param1=8, width=0x100, height=0x1E0)
|
| 1447 |
task_state_machine_init(tsm, 8, 0x100, 0x1E0);
|
| 1448 |
*(tsm as *mut u32) = rb(0x664144); // Override vtable to DisplayGfx
|
| 1449 |
}
|
|
@@ -1588,7 +1567,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1588 |
b"layer\\layer.spr|layer.spr\0".as_ptr(),
|
| 1589 |
);
|
| 1590 |
|
| 1591 |
-
// gradient_image_2 = 0
|
| 1592 |
(*ddgame).gradient_image_2 = core::ptr::null_mut();
|
| 1593 |
|
| 1594 |
// ββ Gradient image (0x030) ββ
|
|
@@ -1605,10 +1583,8 @@ unsafe fn init_graphics_and_resources(
|
|
| 1605 |
call_gfx_find_and_load(land_layer, b"gradient.img\0".as_ptr(), layer3_ctx);
|
| 1606 |
(*ddgame).gradient_image = gradient;
|
| 1607 |
} else {
|
| 1608 |
-
//
|
| 1609 |
-
//
|
| 1610 |
-
// This affects the sky background appearance but not gameplay.
|
| 1611 |
-
// Set gradient_image to null (already zero from memset)
|
| 1612 |
}
|
| 1613 |
|
| 1614 |
// ββ Fill image β fill_pixel (0x7338) ββ
|
|
@@ -1647,10 +1623,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1647 |
call_usercall_ecx(wrapper, LOADING_PROGRESS_TICK_ADDR);
|
| 1648 |
}
|
| 1649 |
// ββ Gradient image stub (DDGame+0x30) ββ
|
| 1650 |
-
//
|
| 1651 |
-
// CTaskLand__InitLandscape reads DDGame+0x30/+0x34 and calls vtable[4](0, i) for each
|
| 1652 |
-
// pixel column. vtable[4] = CTaskOilDrum__ProcessFrame_stub (XOR EAX,EAX; RET 0x8) returns 0.
|
| 1653 |
-
// A minimal stub with [6]=0 (zero-width) causes the loop to not execute at all.
|
| 1654 |
if (*ddgame).gradient_image.is_null() {
|
| 1655 |
let obj = wa_malloc(0x2C);
|
| 1656 |
core::ptr::write_bytes(obj, 0, 0x2C);
|
|
|
|
| 446 |
/// # Safety
|
| 447 |
/// `base` must point to a valid memory region with at least 0x4A4 bytes.
|
| 448 |
pub unsafe fn ddgame_init_render_indices(base: *mut u8) {
|
|
|
|
| 449 |
*(base.add(0xC4) as *mut u32) = 0;
|
| 450 |
|
| 451 |
// eh_vector_constructor_iterator equivalent:
|
|
|
|
| 509 |
core::ptr::write_bytes(buffer, 0, total_size as usize);
|
| 510 |
core::ptr::write_bytes(buffer, 0, total_size as usize);
|
| 511 |
|
|
|
|
| 512 |
let obj = object as *mut u32;
|
| 513 |
*obj.add(0) = rb(0x6640EC); // vtable
|
| 514 |
*obj.add(1) = 0; // unused
|
|
|
|
| 554 |
|
| 555 |
let display = (*wrapper).display as *mut u8;
|
| 556 |
let vt = *(display as *const *const u32);
|
| 557 |
+
// vtable[4]: set layer color
|
| 558 |
let set_color: unsafe extern "thiscall" fn(*mut u8, i32, i32) =
|
| 559 |
core::mem::transmute(*vt.add(4));
|
| 560 |
|
|
|
|
| 819 |
}
|
| 820 |
}
|
| 821 |
|
|
|
|
| 822 |
*(handler.add(0x194) as *mut u32) = 1;
|
| 823 |
|
| 824 |
1 // success
|
|
|
|
| 955 |
|
| 956 |
// ββ 6. Sound available + always-1 flags ββ
|
| 957 |
let is_headless = (*game_info).headless_mode != 0;
|
| 958 |
+
// sound_available enables loading progress bar, message pump, and sound during construction.
|
|
|
|
|
|
|
|
|
|
| 959 |
(*ddgame).sound_available = if is_headless { 0 } else { 1 };
|
| 960 |
(*ddgame)._field_7efc = 1;
|
| 961 |
|
|
|
|
| 987 |
|
| 988 |
/// Second half of the constructor: GfxHandler, landscape, sprites, audio, resources.
|
| 989 |
///
|
| 990 |
+
/// Second half of the constructor β initializes graphics, audio, landscape, and sprites.
|
|
|
|
| 991 |
#[cfg(target_arch = "x86")]
|
| 992 |
unsafe fn init_graphics_and_resources(
|
| 993 |
wrapper: *mut DDGameWrapper,
|
|
|
|
| 999 |
let ddgame = (*wrapper).ddgame;
|
| 1000 |
let fopen: unsafe extern "cdecl" fn(*const u8, *const u8) -> *mut u8 =
|
| 1001 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
| 1002 |
+
let gfx_handler_vtable = rb(0x66B280) as u32; // GfxHandler vtable
|
| 1003 |
|
| 1004 |
// ββ GfxHandler #1 (primary) ββ
|
| 1005 |
let gfx1 = wa_malloc(0x19C);
|
|
|
|
| 1061 |
|
| 1062 |
// ββ GfxHandler #2 (conditional) ββ
|
| 1063 |
let game_version = (*game_info).game_version;
|
|
|
|
| 1064 |
let threshold = if (*wrapper).gfx_mode != 0 { 33 } else { -2i32 };
|
| 1065 |
if game_version < threshold {
|
| 1066 |
let c_digit = if game_version > -3 { b'2' } else { b'1' };
|
|
|
|
| 1088 |
if *(rb(0x88E485) as *const u8) == 0 {
|
| 1089 |
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
| 1090 |
}
|
| 1091 |
+
// Read display (may be modified by FUN_00570A90).
|
|
|
|
|
|
|
| 1092 |
let disp = (*wrapper).display as *mut u8;
|
| 1093 |
let gfx_handler = (*wrapper)._field_4c0;
|
| 1094 |
let vt = *(disp as *const *const u32);
|
| 1095 |
+
// vtable[4]: set palette range
|
| 1096 |
let vt_10: unsafe extern "thiscall" fn(*mut u8, i32, i32) =
|
| 1097 |
core::mem::transmute(*vt.add(4));
|
| 1098 |
vt_10(disp, 1, 0xFE);
|
| 1099 |
+
// vtable[0x1F]: init palette layer
|
| 1100 |
let vt_7c: unsafe extern "thiscall" fn(*mut u8, i32, i32, i32, *mut u8, u32) =
|
| 1101 |
core::mem::transmute(*vt.add(0x1F));
|
| 1102 |
vt_7c(disp, 1, 1, 0, gfx_handler, rb(0x66A3A8));
|
| 1103 |
+
// vtable[0x17]: set layer visibility
|
| 1104 |
let vt_5c: unsafe extern "thiscall" fn(*mut u8, i32, i32) =
|
| 1105 |
core::mem::transmute(*vt.add(0x17));
|
| 1106 |
vt_5c(disp, 1, -100);
|
|
|
|
| 1136 |
|
| 1137 |
// ββ GfxDir color entries DDGame+0x730C..0x732C ββ
|
| 1138 |
if (*wrapper).gfx_mode != 0 {
|
|
|
|
| 1139 |
// The layer_ctx is used as the output buffer, not a plain stack alloc.
|
| 1140 |
let res = gfx_resource_create((*wrapper)._field_4c0, rb(0x66A3B4) as *const u8, layer_ctx);
|
| 1141 |
if !res.is_null() {
|
|
|
|
| 1157 |
core::mem::transmute(*rvt.add(3));
|
| 1158 |
release(res, 1);
|
| 1159 |
}
|
|
|
|
|
|
|
| 1160 |
}
|
| 1161 |
|
| 1162 |
// ββ Secondary GfxDir object (DDGame+0x2C, conditional) ββ
|
|
|
|
| 1183 |
|
| 1184 |
// ββ DDGameWrapper field inits ββ
|
| 1185 |
(*wrapper)._field_4d8 = 0;
|
| 1186 |
+
// Loading progress total: game_info+0x44C controls team count scaling.
|
|
|
|
|
|
|
| 1187 |
if is_headless {
|
| 1188 |
(*wrapper)._field_4dc = 0x2AD;
|
| 1189 |
} else {
|
|
|
|
| 1263 |
(ddgame as *mut u8).add(0x777C), // param 10
|
| 1264 |
(ddgame as *mut u8).add(0x7780), // param 11
|
| 1265 |
);
|
|
|
|
| 1266 |
(*wrapper).landscape = result as *mut PCLandscape;
|
| 1267 |
(*ddgame).landscape = result as *mut PCLandscape;
|
| 1268 |
result
|
|
|
|
| 1290 |
// ββ 8Γ SpriteRegion at DDGame+0x46C..0x488 ββ
|
| 1291 |
// SpriteRegion__Constructor: fastcall(ECX, EDX) + 6 stack(this, p2, p3, p4, p5, p6), RET 0x18
|
| 1292 |
{
|
|
|
|
| 1293 |
// (ddgame_offset, ECX, EDX, p2, p3, p4, p5, p6=gfx_resource)
|
| 1294 |
// p6 is the GfxResource pointer returned by GfxResource__Create_Maybe.
|
| 1295 |
let gr = gfx_resource as u32;
|
|
|
|
| 1419 |
}
|
| 1420 |
|
| 1421 |
// ββ DisplayGfx at DDGame+0x138 ββ
|
|
|
|
|
|
|
| 1422 |
{
|
| 1423 |
let tsm = wa_malloc(0x4C);
|
| 1424 |
core::ptr::write_bytes(tsm, 0, 0x2C);
|
| 1425 |
if !tsm.is_null() {
|
|
|
|
|
|
|
| 1426 |
task_state_machine_init(tsm, 8, 0x100, 0x1E0);
|
| 1427 |
*(tsm as *mut u32) = rb(0x664144); // Override vtable to DisplayGfx
|
| 1428 |
}
|
|
|
|
| 1567 |
b"layer\\layer.spr|layer.spr\0".as_ptr(),
|
| 1568 |
);
|
| 1569 |
|
|
|
|
| 1570 |
(*ddgame).gradient_image_2 = core::ptr::null_mut();
|
| 1571 |
|
| 1572 |
// ββ Gradient image (0x030) ββ
|
|
|
|
| 1583 |
call_gfx_find_and_load(land_layer, b"gradient.img\0".as_ptr(), layer3_ctx);
|
| 1584 |
(*ddgame).gradient_image = gradient;
|
| 1585 |
} else {
|
| 1586 |
+
// TODO: port complex gradient computation (~200 lines of color interpolation).
|
| 1587 |
+
// Affects sky background appearance. Not yet implemented.
|
|
|
|
|
|
|
| 1588 |
}
|
| 1589 |
|
| 1590 |
// ββ Fill image β fill_pixel (0x7338) ββ
|
|
|
|
| 1623 |
call_usercall_ecx(wrapper, LOADING_PROGRESS_TICK_ADDR);
|
| 1624 |
}
|
| 1625 |
// ββ Gradient image stub (DDGame+0x30) ββ
|
| 1626 |
+
// Minimal stub: [6]=0 (zero-width) so CTaskLand skips the gradient column loop.
|
|
|
|
|
|
|
|
|
|
| 1627 |
if (*ddgame).gradient_image.is_null() {
|
| 1628 |
let obj = wa_malloc(0x2C);
|
| 1629 |
core::ptr::write_bytes(obj, 0, 0x2C);
|