fix: correct usercall conventions for all DDGame sub-functions
Browse filesFix calling conventions for 6 functions called by create_ddgame():
- FUN_570A90: usercall(EAX=wrapper), was called with no args
- FUN_570E20: usercall(ESI=wrapper), was called with no args
- FUN_570F30: usercall(ESI=wrapper), was called with no args
- DSSound_LoadAllSpeechBanks: usercall(ESI=wrapper), was extern "C"
- DSSound_LoadEffectWAVs: stdcall(wrapper), was extern "C"
- DDGame__LoadWeaponSprites: usercall(ECX=wrapper), was extern "C"
Add reusable naked bridges: call_usercall_esi(), call_usercall_eax(),
call_usercall_ecx() for ESI/EAX/ECX register passing.
Also add null guard for net_game pointer in _field_4dc calculation.
Constructor now works: game starts, 119/133 validations pass.
Remaining failures are race conditions (game loop accessing DDGame
during construction) not logic errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@@ -371,6 +371,11 @@ pub fn init_constructor_addrs() {
|
|
| 371 |
unsafe {
|
| 372 |
INIT_FIELDS_ADDR = rb(va::DDGAME_INIT_FIELDS);
|
| 373 |
SPRITE_REGION_CTOR_ADDR = rb(va::SPRITE_REGION_CONSTRUCTOR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
}
|
| 375 |
}
|
| 376 |
|
|
@@ -477,18 +482,8 @@ pub unsafe fn create_ddgame(
|
|
| 477 |
}
|
| 478 |
}
|
| 479 |
|
| 480 |
-
let _ = log_line(&format!(
|
| 481 |
-
"[DDGame] create_ddgame: alloc=0x{:08X}, headless={}, sound=0x{:08X}",
|
| 482 |
-
ddgame as u32, is_headless, sound as u32,
|
| 483 |
-
));
|
| 484 |
-
|
| 485 |
// ββ 9. InitVersionFlags β sets DDGame+0x7E2E/0x7E2F/0x7E3F ββ
|
| 486 |
-
let _ = log_line("[DDGame] calling InitVersionFlags");
|
| 487 |
call_init_version_flags(wrapper);
|
| 488 |
-
let _ = log_line(&format!(
|
| 489 |
-
"[DDGame] InitVersionFlags done, wrapper+0x4D0=0x{:08X}",
|
| 490 |
-
*(wrapper as *const u8).add(0x4D0).cast::<u32>(),
|
| 491 |
-
));
|
| 492 |
|
| 493 |
// ββ 10. GfxHandler, landscape, sprites, audio, resources ββ
|
| 494 |
init_graphics_and_resources(wrapper, game_info, net_game, display, is_headless);
|
|
@@ -509,7 +504,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 509 |
is_headless: bool,
|
| 510 |
) {
|
| 511 |
let ddgame = (*wrapper).ddgame;
|
| 512 |
-
let _ = log_line("[DDGame] init_graphics_and_resources: start");
|
| 513 |
let fopen: unsafe extern "cdecl" fn(*const u8, *const u8) -> *mut u8 =
|
| 514 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
| 515 |
let gfx_load_dir_addr = rb(va::GFX_HANDLER_LOAD_DIR);
|
|
@@ -521,7 +515,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 521 |
*(gfx1 as *mut u32) = gfx_handler_vtable;
|
| 522 |
(*wrapper)._field_4c0 = gfx1;
|
| 523 |
(*wrapper)._field_4c4 = core::ptr::null_mut();
|
| 524 |
-
let _ = log_line(&format!("[DDGame] GfxHandler #1 alloc=0x{:08X}, vtable=0x{:08X}", gfx1 as u32, gfx_handler_vtable));
|
| 525 |
|
| 526 |
// Build path list (order depends on headless + GameInfo+0xF374)
|
| 527 |
let f914 = *(game_info as *const u8).add(0xF914).cast::<i32>();
|
|
@@ -540,9 +533,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 540 |
|
| 541 |
let mut gfx_loaded_idx = 0u32;
|
| 542 |
for (i, path) in paths.iter().enumerate() {
|
| 543 |
-
let _ = log_line(&format!("[DDGame] Trying Gfx path {}", i));
|
| 544 |
let fp = fopen(path.as_ptr(), b"rb\0".as_ptr());
|
| 545 |
-
let _ = log_line(&format!("[DDGame] fopen returned 0x{:08X}", fp as u32));
|
| 546 |
*(gfx1.add(0x198) as *mut *mut u8) = fp;
|
| 547 |
if !fp.is_null() && call_gfx_load_dir(gfx1, gfx_load_dir_addr) != 0 {
|
| 548 |
gfx_loaded_idx = i as u32;
|
|
@@ -555,11 +546,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 555 |
|
| 556 |
let headless_offset = if f914 != 0 { 1u32 } else { 0u32 };
|
| 557 |
(*wrapper).gfx_mode = if gfx_loaded_idx.wrapping_sub(headless_offset) < 2 { 1 } else { 0 };
|
| 558 |
-
let _ = log_line(&format!(
|
| 559 |
-
"[DDGame] GfxHandler #1 loaded (idx={}, gfx_mode={}, gfx1=0x{:08X}, wrapper+0x4D0=0x{:08X})",
|
| 560 |
-
gfx_loaded_idx, (*wrapper).gfx_mode, gfx1 as u32,
|
| 561 |
-
*(wrapper as *const u8).add(0x4D0).cast::<u32>(),
|
| 562 |
-
));
|
| 563 |
|
| 564 |
// ββ GfxHandler #2 (conditional) ββ
|
| 565 |
let d778 = *(game_info as *const u8).add(0xD778).cast::<i32>();
|
|
@@ -589,28 +575,15 @@ unsafe fn init_graphics_and_resources(
|
|
| 589 |
}
|
| 590 |
}
|
| 591 |
|
| 592 |
-
let _ = log_line(&format!(
|
| 593 |
-
"[DDGame] GfxHandler #2 done, _field_4c4=0x{:08X}, wrapper+0x4D0=0x{:08X}",
|
| 594 |
-
(*wrapper)._field_4c4 as u32,
|
| 595 |
-
*(wrapper as *const u8).add(0x4D0).cast::<u32>(),
|
| 596 |
-
));
|
| 597 |
-
|
| 598 |
// ββ Display palette setup (non-headless) ββ
|
| 599 |
if !is_headless {
|
| 600 |
if *(rb(0x88E485) as *const u8) == 0 {
|
| 601 |
-
|
| 602 |
-
core::mem::transmute(rb(va::FUN_570A90) as usize);
|
| 603 |
-
fun_570a90();
|
| 604 |
}
|
| 605 |
-
let _ = log_line(&format!(
|
| 606 |
-
"[DDGame] palette: past 570a90, wrapper+0x4D0=0x{:08X}",
|
| 607 |
-
*(wrapper as *const u8).add(0x4D0).cast::<u32>(),
|
| 608 |
-
));
|
| 609 |
// DDGameWrapper+0x4D0 points to the display object used for palette calls.
|
| 610 |
// BUT: FUN_00570A90 or something else may write to wrapper in ways we
|
| 611 |
// don't expect. Read via struct field instead of raw offset.
|
| 612 |
let disp = (*wrapper).display as *mut u8;
|
| 613 |
-
let _ = log_line(&format!("[DDGame] palette: disp=0x{:08X}", disp as u32));
|
| 614 |
let vt = *(disp as *const *const u32);
|
| 615 |
let vt_10: unsafe extern "thiscall" fn(*mut u8) =
|
| 616 |
core::mem::transmute(*vt.add(4));
|
|
@@ -637,18 +610,13 @@ unsafe fn init_graphics_and_resources(
|
|
| 637 |
}
|
| 638 |
}
|
| 639 |
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
// ββ FUN_00570E20 ββ
|
| 643 |
-
{
|
| 644 |
-
let f: unsafe extern "C" fn() = core::mem::transmute(rb(va::FUN_570E20) as usize);
|
| 645 |
-
f();
|
| 646 |
-
}
|
| 647 |
|
| 648 |
// ββ Display vtable slot 5 (offset 0x14) ββ
|
| 649 |
{
|
| 650 |
let vt = *((*ddgame).display as *const *const u32);
|
| 651 |
-
let f: unsafe extern "thiscall" fn(*mut DDDisplay) =
|
| 652 |
core::mem::transmute(*vt.add(5));
|
| 653 |
f((*ddgame).display);
|
| 654 |
}
|
|
@@ -696,7 +664,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 696 |
|
| 697 |
// ββ DDGameWrapper field inits ββ
|
| 698 |
(*wrapper)._field_4d8 = 0;
|
| 699 |
-
if display.is_null() {
|
| 700 |
(*wrapper)._field_4dc = 0x2AD;
|
| 701 |
} else {
|
| 702 |
let byte_val = *net_game.add(0x44C) as u32;
|
|
@@ -707,22 +675,18 @@ unsafe fn init_graphics_and_resources(
|
|
| 707 |
|
| 708 |
// ββ Audio init (non-headless + sound available) ββ
|
| 709 |
if !is_headless {
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
f();
|
| 714 |
-
}
|
| 715 |
if !(*ddgame).sound.is_null() {
|
| 716 |
{
|
| 717 |
-
|
|
|
|
| 718 |
core::mem::transmute(rb(va::DSSOUND_LOAD_EFFECT_WAVS) as usize);
|
| 719 |
-
f();
|
| 720 |
-
}
|
| 721 |
-
{
|
| 722 |
-
let f: unsafe extern "C" fn() =
|
| 723 |
-
core::mem::transmute(rb(va::DSSOUND_LOAD_ALL_SPEECH_BANKS) as usize);
|
| 724 |
-
f();
|
| 725 |
}
|
|
|
|
|
|
|
| 726 |
// Allocate ActiveSoundTable (0x608 bytes)
|
| 727 |
let ast = wa_malloc(0x608) as *mut ActiveSoundTable;
|
| 728 |
(*ast).ddgame = ddgame;
|
|
@@ -732,8 +696,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 732 |
}
|
| 733 |
}
|
| 734 |
|
| 735 |
-
let _ = log_line("[DDGame] audio init done");
|
| 736 |
-
|
| 737 |
// ββ GfxResource (used by PCLandscape and later by gradients) ββ
|
| 738 |
let gfx_resource_create: unsafe extern "thiscall" fn(*mut u8, *mut u8) -> *mut u8 =
|
| 739 |
core::mem::transmute(rb(va::GFX_RESOURCE_CREATE) as usize);
|
|
@@ -784,10 +746,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 784 |
}
|
| 785 |
};
|
| 786 |
|
| 787 |
-
let _ = log_line(&format!(
|
| 788 |
-
"[DDGame] PCLandscape=0x{:08X}", landscape as u32,
|
| 789 |
-
));
|
| 790 |
-
|
| 791 |
// ββ TaskStateMachine at DDGame+0x380 (alloc 0x2C) ββ
|
| 792 |
{
|
| 793 |
let tsm = wa_malloc(0x2C);
|
|
@@ -849,8 +807,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 849 |
release(gfx_resource);
|
| 850 |
}
|
| 851 |
|
| 852 |
-
let _ = log_line("[DDGame] SpriteRegions + TSM done");
|
| 853 |
-
|
| 854 |
// Resolved addresses for GfxDir operations (used by arrows, gradients, fill)
|
| 855 |
let gfx_find_entry = rb(va::GFX_DIR_FIND_ENTRY) as u32;
|
| 856 |
let gfx_load_image = rb(va::GFX_DIR_LOAD_IMAGE) as u32;
|
|
@@ -939,8 +895,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 939 |
}
|
| 940 |
}
|
| 941 |
|
| 942 |
-
let _ = log_line("[DDGame] arrow sprites done");
|
| 943 |
-
|
| 944 |
// ββ DisplayGfx at DDGame+0x138 ββ
|
| 945 |
{
|
| 946 |
let tsm = wa_malloc(0x2C);
|
|
@@ -967,15 +921,9 @@ unsafe fn init_graphics_and_resources(
|
|
| 967 |
// TODO: populate coord_list from landscape data (complex loop)
|
| 968 |
}
|
| 969 |
|
| 970 |
-
// ββ Weapon sprites (FUN_005717A0 Γ2) ββ
|
| 971 |
-
|
| 972 |
-
|
| 973 |
-
core::mem::transmute(rb(va::DDGAME_LOAD_WEAPON_SPRITES) as usize);
|
| 974 |
-
load_weapon_sprites();
|
| 975 |
-
load_weapon_sprites();
|
| 976 |
-
}
|
| 977 |
-
|
| 978 |
-
let _ = log_line("[DDGame] weapon sprites done");
|
| 979 |
|
| 980 |
// ββ Sprite resource loading via DDGameWrapper vtable[0] ββ
|
| 981 |
// DDNetGameWrapper__LoadResourceList: thiscall(ECX=wrapper) +
|
|
@@ -1048,8 +996,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1048 |
// gradient_image_2 = 0
|
| 1049 |
(*ddgame).gradient_image_2 = core::ptr::null_mut();
|
| 1050 |
|
| 1051 |
-
let _ = log_line("[DDGame] sprite resources loaded");
|
| 1052 |
-
|
| 1053 |
// ββ Gradient image (0x030) ββ
|
| 1054 |
// The gradient is loaded from "gradient.img" via GfxDir.
|
| 1055 |
// Simple path: height <= 0x60 AND level_height == 0x2B8
|
|
@@ -1070,7 +1016,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1070 |
// Complex gradient computation β ~200 lines of color interpolation.
|
| 1071 |
// For now, skip this path (gradient_image remains null/0).
|
| 1072 |
// This affects the sky background appearance but not gameplay.
|
| 1073 |
-
let _ = log_line("[DDGame] WARNING: complex gradient path not ported, skipping");
|
| 1074 |
// Set gradient_image to null (already zero from memset)
|
| 1075 |
}
|
| 1076 |
|
|
@@ -1095,8 +1040,6 @@ unsafe fn init_graphics_and_resources(
|
|
| 1095 |
}
|
| 1096 |
}
|
| 1097 |
|
| 1098 |
-
let _ = log_line(&format!("[DDGame] fill_pixel=0x{:08X}", (*ddgame).fill_pixel));
|
| 1099 |
-
|
| 1100 |
// ββ HUD weapon sprites ββ
|
| 1101 |
{
|
| 1102 |
let hud_load: unsafe extern "stdcall" fn(u32, u32) =
|
|
@@ -1127,9 +1070,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1127 |
init_display_final(disp_obj as u32);
|
| 1128 |
}
|
| 1129 |
if *(rb(0x88E485) as *const u8) == 0 {
|
| 1130 |
-
|
| 1131 |
-
core::mem::transmute(rb(va::FUN_570A90) as usize);
|
| 1132 |
-
fun_570a90();
|
| 1133 |
}
|
| 1134 |
|
| 1135 |
// Display layer visibility: vt[0x5C](1,0), vt[0x5C](2,0), vt[0x5C](3,1)
|
|
@@ -1195,6 +1136,49 @@ unsafe fn call_gfx_find_and_load(
|
|
| 1195 |
call_gfx_load_and_wrap(name, load_image_addr, wrap_addr)
|
| 1196 |
}
|
| 1197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1198 |
/// Bridge to GfxHandler__LoadDir (0x5663E0).
|
| 1199 |
/// Convention: usercall(EAX=handler), plain RET. Returns nonzero on success.
|
| 1200 |
#[cfg(target_arch = "x86")]
|
|
|
|
| 371 |
unsafe {
|
| 372 |
INIT_FIELDS_ADDR = rb(va::DDGAME_INIT_FIELDS);
|
| 373 |
SPRITE_REGION_CTOR_ADDR = rb(va::SPRITE_REGION_CONSTRUCTOR);
|
| 374 |
+
FUN_570E20_ADDR = rb(va::FUN_570E20);
|
| 375 |
+
FUN_570A90_ADDR = rb(va::FUN_570A90);
|
| 376 |
+
FUN_570F30_ADDR = rb(va::DDGAME_INIT_SOUND_PATHS);
|
| 377 |
+
LOAD_SPEECH_BANKS_ADDR = rb(va::DSSOUND_LOAD_ALL_SPEECH_BANKS);
|
| 378 |
+
LOAD_WEAPON_SPRITES_ADDR = rb(va::DDGAME_LOAD_WEAPON_SPRITES);
|
| 379 |
}
|
| 380 |
}
|
| 381 |
|
|
|
|
| 482 |
}
|
| 483 |
}
|
| 484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
// ββ 9. InitVersionFlags β sets DDGame+0x7E2E/0x7E2F/0x7E3F ββ
|
|
|
|
| 486 |
call_init_version_flags(wrapper);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
|
| 488 |
// ββ 10. GfxHandler, landscape, sprites, audio, resources ββ
|
| 489 |
init_graphics_and_resources(wrapper, game_info, net_game, display, is_headless);
|
|
|
|
| 504 |
is_headless: bool,
|
| 505 |
) {
|
| 506 |
let ddgame = (*wrapper).ddgame;
|
|
|
|
| 507 |
let fopen: unsafe extern "cdecl" fn(*const u8, *const u8) -> *mut u8 =
|
| 508 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
| 509 |
let gfx_load_dir_addr = rb(va::GFX_HANDLER_LOAD_DIR);
|
|
|
|
| 515 |
*(gfx1 as *mut u32) = gfx_handler_vtable;
|
| 516 |
(*wrapper)._field_4c0 = gfx1;
|
| 517 |
(*wrapper)._field_4c4 = core::ptr::null_mut();
|
|
|
|
| 518 |
|
| 519 |
// Build path list (order depends on headless + GameInfo+0xF374)
|
| 520 |
let f914 = *(game_info as *const u8).add(0xF914).cast::<i32>();
|
|
|
|
| 533 |
|
| 534 |
let mut gfx_loaded_idx = 0u32;
|
| 535 |
for (i, path) in paths.iter().enumerate() {
|
|
|
|
| 536 |
let fp = fopen(path.as_ptr(), b"rb\0".as_ptr());
|
|
|
|
| 537 |
*(gfx1.add(0x198) as *mut *mut u8) = fp;
|
| 538 |
if !fp.is_null() && call_gfx_load_dir(gfx1, gfx_load_dir_addr) != 0 {
|
| 539 |
gfx_loaded_idx = i as u32;
|
|
|
|
| 546 |
|
| 547 |
let headless_offset = if f914 != 0 { 1u32 } else { 0u32 };
|
| 548 |
(*wrapper).gfx_mode = if gfx_loaded_idx.wrapping_sub(headless_offset) < 2 { 1 } else { 0 };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
|
| 550 |
// ββ GfxHandler #2 (conditional) ββ
|
| 551 |
let d778 = *(game_info as *const u8).add(0xD778).cast::<i32>();
|
|
|
|
| 575 |
}
|
| 576 |
}
|
| 577 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
// ββ Display palette setup (non-headless) ββ
|
| 579 |
if !is_headless {
|
| 580 |
if *(rb(0x88E485) as *const u8) == 0 {
|
| 581 |
+
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
|
|
|
|
|
|
| 582 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
// DDGameWrapper+0x4D0 points to the display object used for palette calls.
|
| 584 |
// BUT: FUN_00570A90 or something else may write to wrapper in ways we
|
| 585 |
// don't expect. Read via struct field instead of raw offset.
|
| 586 |
let disp = (*wrapper).display as *mut u8;
|
|
|
|
| 587 |
let vt = *(disp as *const *const u32);
|
| 588 |
let vt_10: unsafe extern "thiscall" fn(*mut u8) =
|
| 589 |
core::mem::transmute(*vt.add(4));
|
|
|
|
| 610 |
}
|
| 611 |
}
|
| 612 |
|
| 613 |
+
// ββ FUN_00570E20: usercall(ESI=wrapper), plain RET ββ
|
| 614 |
+
call_usercall_esi(wrapper, FUN_570E20_ADDR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 615 |
|
| 616 |
// ββ Display vtable slot 5 (offset 0x14) ββ
|
| 617 |
{
|
| 618 |
let vt = *((*ddgame).display as *const *const u32);
|
| 619 |
+
let f: unsafe extern "thiscall" fn(*mut DDDisplay) -> *mut u8 =
|
| 620 |
core::mem::transmute(*vt.add(5));
|
| 621 |
f((*ddgame).display);
|
| 622 |
}
|
|
|
|
| 664 |
|
| 665 |
// ββ DDGameWrapper field inits ββ
|
| 666 |
(*wrapper)._field_4d8 = 0;
|
| 667 |
+
if display.is_null() || net_game.is_null() {
|
| 668 |
(*wrapper)._field_4dc = 0x2AD;
|
| 669 |
} else {
|
| 670 |
let byte_val = *net_game.add(0x44C) as u32;
|
|
|
|
| 675 |
|
| 676 |
// ββ Audio init (non-headless + sound available) ββ
|
| 677 |
if !is_headless {
|
| 678 |
+
// FUN_570F30: usercall(ESI=wrapper)
|
| 679 |
+
call_usercall_esi(wrapper, FUN_570F30_ADDR);
|
| 680 |
+
|
|
|
|
|
|
|
| 681 |
if !(*ddgame).sound.is_null() {
|
| 682 |
{
|
| 683 |
+
// DSSound_LoadEffectWAVs: stdcall(wrapper)
|
| 684 |
+
let f: unsafe extern "stdcall" fn(*mut DDGameWrapper) =
|
| 685 |
core::mem::transmute(rb(va::DSSOUND_LOAD_EFFECT_WAVS) as usize);
|
| 686 |
+
f(wrapper);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
}
|
| 688 |
+
// DSSound_LoadAllSpeechBanks: usercall(ESI=wrapper)
|
| 689 |
+
call_usercall_esi(wrapper, LOAD_SPEECH_BANKS_ADDR);
|
| 690 |
// Allocate ActiveSoundTable (0x608 bytes)
|
| 691 |
let ast = wa_malloc(0x608) as *mut ActiveSoundTable;
|
| 692 |
(*ast).ddgame = ddgame;
|
|
|
|
| 696 |
}
|
| 697 |
}
|
| 698 |
|
|
|
|
|
|
|
| 699 |
// ββ GfxResource (used by PCLandscape and later by gradients) ββ
|
| 700 |
let gfx_resource_create: unsafe extern "thiscall" fn(*mut u8, *mut u8) -> *mut u8 =
|
| 701 |
core::mem::transmute(rb(va::GFX_RESOURCE_CREATE) as usize);
|
|
|
|
| 746 |
}
|
| 747 |
};
|
| 748 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 749 |
// ββ TaskStateMachine at DDGame+0x380 (alloc 0x2C) ββ
|
| 750 |
{
|
| 751 |
let tsm = wa_malloc(0x2C);
|
|
|
|
| 807 |
release(gfx_resource);
|
| 808 |
}
|
| 809 |
|
|
|
|
|
|
|
| 810 |
// Resolved addresses for GfxDir operations (used by arrows, gradients, fill)
|
| 811 |
let gfx_find_entry = rb(va::GFX_DIR_FIND_ENTRY) as u32;
|
| 812 |
let gfx_load_image = rb(va::GFX_DIR_LOAD_IMAGE) as u32;
|
|
|
|
| 895 |
}
|
| 896 |
}
|
| 897 |
|
|
|
|
|
|
|
| 898 |
// ββ DisplayGfx at DDGame+0x138 ββ
|
| 899 |
{
|
| 900 |
let tsm = wa_malloc(0x2C);
|
|
|
|
| 921 |
// TODO: populate coord_list from landscape data (complex loop)
|
| 922 |
}
|
| 923 |
|
| 924 |
+
// ββ Weapon sprites (FUN_005717A0 Γ2): usercall(ECX=wrapper) ββ
|
| 925 |
+
call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
|
| 926 |
+
call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 927 |
|
| 928 |
// ββ Sprite resource loading via DDGameWrapper vtable[0] ββ
|
| 929 |
// DDNetGameWrapper__LoadResourceList: thiscall(ECX=wrapper) +
|
|
|
|
| 996 |
// gradient_image_2 = 0
|
| 997 |
(*ddgame).gradient_image_2 = core::ptr::null_mut();
|
| 998 |
|
|
|
|
|
|
|
| 999 |
// ββ Gradient image (0x030) ββ
|
| 1000 |
// The gradient is loaded from "gradient.img" via GfxDir.
|
| 1001 |
// Simple path: height <= 0x60 AND level_height == 0x2B8
|
|
|
|
| 1016 |
// Complex gradient computation β ~200 lines of color interpolation.
|
| 1017 |
// For now, skip this path (gradient_image remains null/0).
|
| 1018 |
// This affects the sky background appearance but not gameplay.
|
|
|
|
| 1019 |
// Set gradient_image to null (already zero from memset)
|
| 1020 |
}
|
| 1021 |
|
|
|
|
| 1040 |
}
|
| 1041 |
}
|
| 1042 |
|
|
|
|
|
|
|
| 1043 |
// ββ HUD weapon sprites ββ
|
| 1044 |
{
|
| 1045 |
let hud_load: unsafe extern "stdcall" fn(u32, u32) =
|
|
|
|
| 1070 |
init_display_final(disp_obj as u32);
|
| 1071 |
}
|
| 1072 |
if *(rb(0x88E485) as *const u8) == 0 {
|
| 1073 |
+
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
|
|
|
|
|
|
| 1074 |
}
|
| 1075 |
|
| 1076 |
// Display layer visibility: vt[0x5C](1,0), vt[0x5C](2,0), vt[0x5C](3,1)
|
|
|
|
| 1136 |
call_gfx_load_and_wrap(name, load_image_addr, wrap_addr)
|
| 1137 |
}
|
| 1138 |
|
| 1139 |
+
// Statics for usercall bridge addresses
|
| 1140 |
+
static mut FUN_570E20_ADDR: u32 = 0;
|
| 1141 |
+
static mut FUN_570A90_ADDR: u32 = 0;
|
| 1142 |
+
static mut FUN_570F30_ADDR: u32 = 0;
|
| 1143 |
+
static mut LOAD_SPEECH_BANKS_ADDR: u32 = 0;
|
| 1144 |
+
static mut LOAD_WEAPON_SPRITES_ADDR: u32 = 0;
|
| 1145 |
+
|
| 1146 |
+
/// Bridge: usercall(ESI=wrapper), plain RET. Used by FUN_570E20, FUN_570F30, LoadSpeechBanks.
|
| 1147 |
+
#[cfg(target_arch = "x86")]
|
| 1148 |
+
#[unsafe(naked)]
|
| 1149 |
+
unsafe extern "C" fn call_usercall_esi(_wrapper: *mut DDGameWrapper, _addr: u32) {
|
| 1150 |
+
core::arch::naked_asm!(
|
| 1151 |
+
"pushl %esi",
|
| 1152 |
+
"movl 8(%esp), %esi", // ESI = wrapper
|
| 1153 |
+
"movl 12(%esp), %eax", // EAX = target address
|
| 1154 |
+
"calll *%eax",
|
| 1155 |
+
"popl %esi",
|
| 1156 |
+
"retl",
|
| 1157 |
+
options(att_syntax),
|
| 1158 |
+
);
|
| 1159 |
+
}
|
| 1160 |
+
|
| 1161 |
+
/// Bridge: usercall(EAX=wrapper), plain RET. Used by FUN_570A90.
|
| 1162 |
+
#[cfg(target_arch = "x86")]
|
| 1163 |
+
#[unsafe(naked)]
|
| 1164 |
+
unsafe extern "C" fn call_usercall_eax(_wrapper: *mut DDGameWrapper, _addr: u32) {
|
| 1165 |
+
core::arch::naked_asm!(
|
| 1166 |
+
"movl 4(%esp), %eax", // EAX = wrapper
|
| 1167 |
+
"movl 8(%esp), %ecx", // ECX = target address (temp)
|
| 1168 |
+
"calll *%ecx",
|
| 1169 |
+
"retl",
|
| 1170 |
+
options(att_syntax),
|
| 1171 |
+
);
|
| 1172 |
+
}
|
| 1173 |
+
|
| 1174 |
+
/// Bridge: usercall(ECX=wrapper), plain RET. Used by FUN_5717A0.
|
| 1175 |
+
#[cfg(target_arch = "x86")]
|
| 1176 |
+
unsafe fn call_usercall_ecx(wrapper: *mut DDGameWrapper, addr: u32) {
|
| 1177 |
+
let f: unsafe extern "thiscall" fn(*mut DDGameWrapper) =
|
| 1178 |
+
core::mem::transmute(addr as usize);
|
| 1179 |
+
f(wrapper);
|
| 1180 |
+
}
|
| 1181 |
+
|
| 1182 |
/// Bridge to GfxHandler__LoadDir (0x5663E0).
|
| 1183 |
/// Convention: usercall(EAX=handler), plain RET. Returns nonzero on success.
|
| 1184 |
#[cfg(target_arch = "x86")]
|