fix: enable arrow sprite loading fallback, fix SpriteRegion param order
Browse files- Arrow loading now falls back to GfxDir__LoadImage when cached==0,
matching the original constructor behavior
- Fix arrow SpriteRegion params: EDX=half_h, p4=half_w (constructor
computes this[4]=EDX-p3 for height, this[3]=p4-p2 for width)
- Move gfx_resource release AFTER arrow loop (arrows need it for
SpriteRegion construction)
- Pass gfx_resource to arrow SpriteRegions as p6
Shadow comparison now shows only 3 field differences vs original
(was 67): color entries, +0x7338, and +0x041C/0x0420.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -1388,19 +1388,9 @@ unsafe fn init_graphics_and_resources(
|
|
| 1388 |
*((ddgame as *mut u8).add(0x468) as *mut u32) = get_val(landscape);
|
| 1389 |
}
|
| 1390 |
|
| 1391 |
-
//
|
| 1392 |
-
//
|
| 1393 |
-
if !gfx_resource.is_null() {
|
| 1394 |
-
let rvt = *(gfx_resource as *const *const u32);
|
| 1395 |
-
let release: unsafe extern "thiscall" fn(*mut u8, u8) =
|
| 1396 |
-
core::mem::transmute(*rvt.add(3));
|
| 1397 |
-
release(gfx_resource, 1);
|
| 1398 |
-
}
|
| 1399 |
|
| 1400 |
-
// GfxDir addresses are resolved via statics (GFX_FIND_ENTRY_ADDR, GFX_LOAD_IMAGE_ADDR)
|
| 1401 |
-
// and called through naked bridges (call_gfx_find_entry, call_gfx_load_image).
|
| 1402 |
-
|
| 1403 |
-
// return moved further up
|
| 1404 |
// ββ Arrow sprites + collision regions (32 iterations) ββ
|
| 1405 |
{
|
| 1406 |
let gfx_handler = (*wrapper)._field_4c0;
|
|
@@ -1446,12 +1436,12 @@ unsafe fn init_graphics_and_resources(
|
|
| 1446 |
core::mem::transmute(rb(0x4F5E80) as usize);
|
| 1447 |
sprite = ctor(cached);
|
| 1448 |
} else {
|
| 1449 |
-
// Fallback: load from file
|
| 1450 |
-
sprite =
|
| 1451 |
}
|
| 1452 |
} else {
|
| 1453 |
-
// Entry not found β
|
| 1454 |
-
sprite =
|
| 1455 |
}
|
| 1456 |
if i == 0 {
|
| 1457 |
let _ = crate::log::log_line(&format!(
|
|
@@ -1472,7 +1462,19 @@ unsafe fn init_graphics_and_resources(
|
|
| 1472 |
let alloc = wa_malloc(0x9C);
|
| 1473 |
core::ptr::write_bytes(alloc, 0, 0x9C);
|
| 1474 |
let region = if !alloc.is_null() {
|
| 1475 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1476 |
} else {
|
| 1477 |
core::ptr::null_mut()
|
| 1478 |
};
|
|
@@ -1490,6 +1492,16 @@ unsafe fn init_graphics_and_resources(
|
|
| 1490 |
}
|
| 1491 |
|
| 1492 |
let _ = crate::log::log_line("[DDGame] arrow loop done");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1493 |
// ββ DisplayGfx at DDGame+0x138 ββ
|
| 1494 |
// Original: alloc 0x4C, memset 0x2C, PUSH 0x100, MOV ECX=8, MOV EDI=0x1E0,
|
| 1495 |
// CALL 0x4F6370, MOV [alloc]=0x664144
|
|
|
|
| 1388 |
*((ddgame as *mut u8).add(0x468) as *mut u32) = get_val(landscape);
|
| 1389 |
}
|
| 1390 |
|
| 1391 |
+
// NOTE: gfx_resource is NOT released here β arrow SpriteRegions need it.
|
| 1392 |
+
// It's released after the arrow loop below.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1393 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1394 |
// ββ Arrow sprites + collision regions (32 iterations) ββ
|
| 1395 |
{
|
| 1396 |
let gfx_handler = (*wrapper)._field_4c0;
|
|
|
|
| 1436 |
core::mem::transmute(rb(0x4F5E80) as usize);
|
| 1437 |
sprite = ctor(cached);
|
| 1438 |
} else {
|
| 1439 |
+
// Fallback: load from file via GfxDir__LoadImage + IMG_Decode
|
| 1440 |
+
sprite = call_gfx_load_and_wrap(gfx_handler, name_buf.as_ptr(), layer_ctx);
|
| 1441 |
}
|
| 1442 |
} else {
|
| 1443 |
+
// Entry not found β try direct file load
|
| 1444 |
+
sprite = call_gfx_load_and_wrap(gfx_handler, name_buf.as_ptr(), layer_ctx);
|
| 1445 |
}
|
| 1446 |
if i == 0 {
|
| 1447 |
let _ = crate::log::log_line(&format!(
|
|
|
|
| 1462 |
let alloc = wa_malloc(0x9C);
|
| 1463 |
core::ptr::write_bytes(alloc, 0, 0x9C);
|
| 1464 |
let region = if !alloc.is_null() {
|
| 1465 |
+
// SpriteRegion params: ECX, EDX, this, p2, p3, p4, p5, p6
|
| 1466 |
+
// this[3] = p4 - p2 (width), this[4] = EDX - p3 (height)
|
| 1467 |
+
// For arrows: region from (0,0) to (half_w, half_h)
|
| 1468 |
+
call_sprite_region_ctor(
|
| 1469 |
+
alloc,
|
| 1470 |
+
0, // ECX (x_max for this[5])
|
| 1471 |
+
half_h as u32, // EDX (y_max β this[4] = EDX - p3)
|
| 1472 |
+
0, // p2 (x_offset)
|
| 1473 |
+
0, // p3 (y_offset)
|
| 1474 |
+
half_w as u32, // p4 (x_limit β this[3] = p4 - p2)
|
| 1475 |
+
half_h as u32, // p5 (y_limit for this[6])
|
| 1476 |
+
gfx_resource as u32, // p6 (gfx_resource)
|
| 1477 |
+
)
|
| 1478 |
} else {
|
| 1479 |
core::ptr::null_mut()
|
| 1480 |
};
|
|
|
|
| 1492 |
}
|
| 1493 |
|
| 1494 |
let _ = crate::log::log_line("[DDGame] arrow loop done");
|
| 1495 |
+
|
| 1496 |
+
// Release gfx_resource AFTER arrow loop (arrows need it for SpriteRegions)
|
| 1497 |
+
// vtable[3] = DisplayGfx__vmethod_3: thiscall(this, byte param_2), RET 4.
|
| 1498 |
+
if !gfx_resource.is_null() {
|
| 1499 |
+
let rvt = *(gfx_resource as *const *const u32);
|
| 1500 |
+
let release: unsafe extern "thiscall" fn(*mut u8, u8) =
|
| 1501 |
+
core::mem::transmute(*rvt.add(3));
|
| 1502 |
+
release(gfx_resource, 1);
|
| 1503 |
+
}
|
| 1504 |
+
|
| 1505 |
// ββ DisplayGfx at DDGame+0x138 ββ
|
| 1506 |
// Original: alloc 0x4C, memset 0x2C, PUSH 0x100, MOV ECX=8, MOV EDI=0x1E0,
|
| 1507 |
// CALL 0x4F6370, MOV [alloc]=0x664144
|