refactor: use *const c_char for C string parameters
Browse filesChange string parameters from *const u8 to *const c_char in:
- gfx_dir_find_entry, gfx_resource_create, call_gfx_load_and_wrap
- call_gfx_load_image (naked asm bridge)
- DDDisplay::load_sprite, DDDisplay::load_sprite_by_layer
- DDDisplayVtable function pointer types
- fopen transmute in ddgame.rs
- ddgame_init.rs hook trampolines
Internal byte-level operations (gfx_dir_hash, buffer manipulation)
keep *const u8 with .cast() at boundaries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/display/dd_display.rs
CHANGED
|
@@ -84,8 +84,14 @@ pub struct DDDisplayVtable {
|
|
| 84 |
/// Slot 30
|
| 85 |
pub _slot_30: usize,
|
| 86 |
/// Slot 31 (0x7C): load sprite with flag β thiscall(this, layer, id, flag, gfx, name), RET 0x14
|
| 87 |
-
pub load_sprite:
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
/// Slot 32
|
| 90 |
pub _slot_32: usize,
|
| 91 |
/// Slot 33
|
|
@@ -97,8 +103,13 @@ pub struct DDDisplayVtable {
|
|
| 97 |
/// Slot 36
|
| 98 |
pub _slot_36: usize,
|
| 99 |
/// Slot 37 (0x94): load sprite by layer β thiscall(this, layer, id, gfx, name), RET 0x10
|
| 100 |
-
pub load_sprite_by_layer:
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
const _: () = assert!(core::mem::size_of::<DDDisplayVtable>() == 38 * 4);
|
|
@@ -131,7 +142,7 @@ impl DDDisplay {
|
|
| 131 |
layer: u32,
|
| 132 |
id: u32,
|
| 133 |
gfx: *mut u8,
|
| 134 |
-
name: *const
|
| 135 |
) -> i32 {
|
| 136 |
((*(*this).vtable).load_sprite_by_layer)(this, layer, id, gfx, name)
|
| 137 |
}
|
|
@@ -144,7 +155,7 @@ impl DDDisplay {
|
|
| 144 |
id: u32,
|
| 145 |
flag: u32,
|
| 146 |
gfx: *mut u8,
|
| 147 |
-
name: *const
|
| 148 |
) -> i32 {
|
| 149 |
((*(*this).vtable).load_sprite)(this, layer, id, flag, gfx, name)
|
| 150 |
}
|
|
|
|
| 84 |
/// Slot 30
|
| 85 |
pub _slot_30: usize,
|
| 86 |
/// Slot 31 (0x7C): load sprite with flag β thiscall(this, layer, id, flag, gfx, name), RET 0x14
|
| 87 |
+
pub load_sprite: unsafe extern "thiscall" fn(
|
| 88 |
+
*mut DDDisplay,
|
| 89 |
+
u32,
|
| 90 |
+
u32,
|
| 91 |
+
u32,
|
| 92 |
+
*mut u8,
|
| 93 |
+
*const core::ffi::c_char,
|
| 94 |
+
) -> i32,
|
| 95 |
/// Slot 32
|
| 96 |
pub _slot_32: usize,
|
| 97 |
/// Slot 33
|
|
|
|
| 103 |
/// Slot 36
|
| 104 |
pub _slot_36: usize,
|
| 105 |
/// Slot 37 (0x94): load sprite by layer β thiscall(this, layer, id, gfx, name), RET 0x10
|
| 106 |
+
pub load_sprite_by_layer: unsafe extern "thiscall" fn(
|
| 107 |
+
*mut DDDisplay,
|
| 108 |
+
u32,
|
| 109 |
+
u32,
|
| 110 |
+
*mut u8,
|
| 111 |
+
*const core::ffi::c_char,
|
| 112 |
+
) -> i32,
|
| 113 |
}
|
| 114 |
|
| 115 |
const _: () = assert!(core::mem::size_of::<DDDisplayVtable>() == 38 * 4);
|
|
|
|
| 142 |
layer: u32,
|
| 143 |
id: u32,
|
| 144 |
gfx: *mut u8,
|
| 145 |
+
name: *const core::ffi::c_char,
|
| 146 |
) -> i32 {
|
| 147 |
((*(*this).vtable).load_sprite_by_layer)(this, layer, id, gfx, name)
|
| 148 |
}
|
|
|
|
| 155 |
id: u32,
|
| 156 |
flag: u32,
|
| 157 |
gfx: *mut u8,
|
| 158 |
+
name: *const core::ffi::c_char,
|
| 159 |
) -> i32 {
|
| 160 |
((*(*this).vtable).load_sprite)(this, layer, id, flag, gfx, name)
|
| 161 |
}
|
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -740,7 +740,8 @@ unsafe fn init_graphics_and_resources(
|
|
| 740 |
is_headless: bool,
|
| 741 |
) {
|
| 742 |
let ddgame = (*wrapper).ddgame;
|
| 743 |
-
|
|
|
|
| 744 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
| 745 |
let gfx_dir_vtable = rb(va::GFX_DIR_VTABLE) as u32;
|
| 746 |
|
|
@@ -752,37 +753,37 @@ unsafe fn init_graphics_and_resources(
|
|
| 752 |
// Build path list (order depends on headless + display_flags)
|
| 753 |
let headless = (*game_info).headless_mode as i32;
|
| 754 |
let display_flags = (*game_info).display_flags as i32;
|
| 755 |
-
let paths: [&
|
| 756 |
if display_flags == 0 {
|
| 757 |
[
|
| 758 |
-
c"data\\Gfx\\Gfx.dir"
|
| 759 |
-
c"data\\Gfx\\Gfx0.dir"
|
| 760 |
-
c"data\\Gfx\\Gfx1.dir"
|
| 761 |
]
|
| 762 |
} else {
|
| 763 |
[
|
| 764 |
-
c"data\\Gfx\\Gfx.dir"
|
| 765 |
-
c"data\\Gfx\\Gfx1.dir"
|
| 766 |
-
c"data\\Gfx\\Gfx0.dir"
|
| 767 |
]
|
| 768 |
}
|
| 769 |
} else if display_flags == 0 {
|
| 770 |
[
|
| 771 |
-
c"data\\Gfx\\Gfx0.dir"
|
| 772 |
-
c"data\\Gfx\\Gfx1.dir"
|
| 773 |
-
c"data\\Gfx\\Gfx.dir"
|
| 774 |
]
|
| 775 |
} else {
|
| 776 |
[
|
| 777 |
-
c"data\\Gfx\\Gfx1.dir"
|
| 778 |
-
c"data\\Gfx\\Gfx0.dir"
|
| 779 |
-
c"data\\Gfx\\Gfx.dir"
|
| 780 |
]
|
| 781 |
};
|
| 782 |
|
| 783 |
let mut gfx_loaded_idx = 0u32;
|
| 784 |
for (i, path) in paths.iter().enumerate() {
|
| 785 |
-
let fp = fopen(path.as_ptr(), c"rb".as_ptr()
|
| 786 |
(*gfx1).file_handle = fp;
|
| 787 |
if !fp.is_null()
|
| 788 |
&& call_gfx_load_dir(gfx1 as *mut u8, crate::render::gfx_dir::gfx_load_dir_addr()) != 0
|
|
@@ -813,11 +814,11 @@ unsafe fn init_graphics_and_resources(
|
|
| 813 |
let gfx2 = GfxDir::alloc(gfx_dir_vtable);
|
| 814 |
(*wrapper).secondary_gfx_dir = gfx2 as *mut u8;
|
| 815 |
|
| 816 |
-
let fp = fopen(gfx_c_path.as_ptr(), c"rb".as_ptr()
|
| 817 |
(*gfx2).file_handle = fp;
|
| 818 |
let load_addr = crate::render::gfx_dir::gfx_load_dir_addr();
|
| 819 |
if fp.is_null() || call_gfx_load_dir(gfx2 as *mut u8, load_addr) == 0 {
|
| 820 |
-
let fp2 = fopen(c"data\\Gfx\\Gfx.dir".as_ptr()
|
| 821 |
(*gfx2).file_handle = fp2;
|
| 822 |
if fp2.is_null() || call_gfx_load_dir(gfx2 as *mut u8, load_addr) == 0 {
|
| 823 |
panic!("DDGame: couldn't open secondary Gfx.dir");
|
|
@@ -827,13 +828,20 @@ unsafe fn init_graphics_and_resources(
|
|
| 827 |
|
| 828 |
// ββ Display palette setup (non-headless) ββ
|
| 829 |
if !is_headless {
|
| 830 |
-
if *(rb(va::G_DISPLAY_MODE_FLAG) as *const
|
| 831 |
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
| 832 |
}
|
| 833 |
let disp = (*wrapper).display;
|
| 834 |
let gfx_dir = (*wrapper).primary_gfx_dir;
|
| 835 |
DDDisplay::set_layer_color(disp, 1, 0xFE);
|
| 836 |
-
DDDisplay::load_sprite(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 837 |
DDDisplay::set_layer_visibility(disp, 1, -100);
|
| 838 |
|
| 839 |
// Palette slot range init (raw byte offsets into DisplayBase)
|
|
@@ -866,7 +874,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 866 |
// The layer_ctx is used as the output buffer, not a plain stack alloc.
|
| 867 |
let res = gfx_resource_create(
|
| 868 |
(*wrapper).primary_gfx_dir,
|
| 869 |
-
rb(va::STR_COLOURS_IMG) as *const
|
| 870 |
layer_ctx,
|
| 871 |
);
|
| 872 |
if !res.is_null() {
|
|
@@ -943,7 +951,8 @@ unsafe fn init_graphics_and_resources(
|
|
| 943 |
let gfx_dir = (*wrapper).primary_gfx_dir;
|
| 944 |
let out_buf = wa_malloc(0x900);
|
| 945 |
core::ptr::write_bytes(out_buf, 0, 0x900);
|
| 946 |
-
gfx_resource =
|
|
|
|
| 947 |
}
|
| 948 |
|
| 949 |
// ββ PCLandscape (alloc 0xB44, stdcall 11 params) ββ
|
|
@@ -1048,7 +1057,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1048 |
|
| 1049 |
let layer_ctx = DDDisplay::set_active_layer((*ddgame).display, 1);
|
| 1050 |
|
| 1051 |
-
let entry = gfx_dir_find_entry(name_buf.as_ptr(), gfx_dir);
|
| 1052 |
|
| 1053 |
let sprite: *mut u8;
|
| 1054 |
if !entry.is_null() {
|
|
@@ -1062,11 +1071,11 @@ unsafe fn init_graphics_and_resources(
|
|
| 1062 |
sprite = wa_displaygfx_ctor(cached);
|
| 1063 |
} else {
|
| 1064 |
// Fallback: load from file via GfxDir__LoadImage + IMG_Decode
|
| 1065 |
-
sprite = call_gfx_load_and_wrap(gfx_dir, name_buf.as_ptr(), layer_ctx);
|
| 1066 |
}
|
| 1067 |
} else {
|
| 1068 |
// Entry not found β try direct file load
|
| 1069 |
-
sprite = call_gfx_load_and_wrap(gfx_dir, name_buf.as_ptr(), layer_ctx);
|
| 1070 |
}
|
| 1071 |
|
| 1072 |
// Store arrow sprite at DDGame+0x38+i*4
|
|
@@ -1244,7 +1253,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1244 |
land_layer,
|
| 1245 |
c"back.spr".as_ptr().cast(),
|
| 1246 |
);
|
| 1247 |
-
DDDisplay::load_sprite(disp, 3, 0x26E, 0, land_layer, c"debris.spr".as_ptr()
|
| 1248 |
}
|
| 1249 |
|
| 1250 |
DDDisplay::load_sprite_by_layer(
|
|
@@ -1322,7 +1331,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 1322 |
}
|
| 1323 |
|
| 1324 |
// ββ FUN_00570A90 (second call, conditional) ββ
|
| 1325 |
-
if *(rb(va::G_DISPLAY_MODE_FLAG) as *const
|
| 1326 |
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
| 1327 |
}
|
| 1328 |
|
|
@@ -1461,7 +1470,7 @@ pub mod offsets {
|
|
| 1461 |
pub const TEAM_BLOCKS: usize = 0x4090;
|
| 1462 |
|
| 1463 |
/// Byte offset from TeamArenaState base back to FullTeamBlock array start.
|
| 1464 |
-
/// `blocks_ptr = (tws_base as *const
|
| 1465 |
///
|
| 1466 |
/// entry_ptr(0) = DDGame+0x4628 = TEAM_BLOCKS + 0x598.
|
| 1467 |
/// 0x598 = sizeof(FullTeamBlock) + 0x7C = one block + offset into TeamBlockHeader.
|
|
|
|
| 740 |
is_headless: bool,
|
| 741 |
) {
|
| 742 |
let ddgame = (*wrapper).ddgame;
|
| 743 |
+
use core::ffi::c_char;
|
| 744 |
+
let fopen: unsafe extern "cdecl" fn(*const c_char, *const c_char) -> *mut u8 =
|
| 745 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
| 746 |
let gfx_dir_vtable = rb(va::GFX_DIR_VTABLE) as u32;
|
| 747 |
|
|
|
|
| 753 |
// Build path list (order depends on headless + display_flags)
|
| 754 |
let headless = (*game_info).headless_mode as i32;
|
| 755 |
let display_flags = (*game_info).display_flags as i32;
|
| 756 |
+
let paths: [&core::ffi::CStr; 3] = if headless != 0 {
|
| 757 |
if display_flags == 0 {
|
| 758 |
[
|
| 759 |
+
c"data\\Gfx\\Gfx.dir",
|
| 760 |
+
c"data\\Gfx\\Gfx0.dir",
|
| 761 |
+
c"data\\Gfx\\Gfx1.dir",
|
| 762 |
]
|
| 763 |
} else {
|
| 764 |
[
|
| 765 |
+
c"data\\Gfx\\Gfx.dir",
|
| 766 |
+
c"data\\Gfx\\Gfx1.dir",
|
| 767 |
+
c"data\\Gfx\\Gfx0.dir",
|
| 768 |
]
|
| 769 |
}
|
| 770 |
} else if display_flags == 0 {
|
| 771 |
[
|
| 772 |
+
c"data\\Gfx\\Gfx0.dir",
|
| 773 |
+
c"data\\Gfx\\Gfx1.dir",
|
| 774 |
+
c"data\\Gfx\\Gfx.dir",
|
| 775 |
]
|
| 776 |
} else {
|
| 777 |
[
|
| 778 |
+
c"data\\Gfx\\Gfx1.dir",
|
| 779 |
+
c"data\\Gfx\\Gfx0.dir",
|
| 780 |
+
c"data\\Gfx\\Gfx.dir",
|
| 781 |
]
|
| 782 |
};
|
| 783 |
|
| 784 |
let mut gfx_loaded_idx = 0u32;
|
| 785 |
for (i, path) in paths.iter().enumerate() {
|
| 786 |
+
let fp = fopen(path.as_ptr(), c"rb".as_ptr());
|
| 787 |
(*gfx1).file_handle = fp;
|
| 788 |
if !fp.is_null()
|
| 789 |
&& call_gfx_load_dir(gfx1 as *mut u8, crate::render::gfx_dir::gfx_load_dir_addr()) != 0
|
|
|
|
| 814 |
let gfx2 = GfxDir::alloc(gfx_dir_vtable);
|
| 815 |
(*wrapper).secondary_gfx_dir = gfx2 as *mut u8;
|
| 816 |
|
| 817 |
+
let fp = fopen(gfx_c_path.as_ptr().cast(), c"rb".as_ptr());
|
| 818 |
(*gfx2).file_handle = fp;
|
| 819 |
let load_addr = crate::render::gfx_dir::gfx_load_dir_addr();
|
| 820 |
if fp.is_null() || call_gfx_load_dir(gfx2 as *mut u8, load_addr) == 0 {
|
| 821 |
+
let fp2 = fopen(c"data\\Gfx\\Gfx.dir".as_ptr(), c"rb".as_ptr());
|
| 822 |
(*gfx2).file_handle = fp2;
|
| 823 |
if fp2.is_null() || call_gfx_load_dir(gfx2 as *mut u8, load_addr) == 0 {
|
| 824 |
panic!("DDGame: couldn't open secondary Gfx.dir");
|
|
|
|
| 828 |
|
| 829 |
// ββ Display palette setup (non-headless) ββ
|
| 830 |
if !is_headless {
|
| 831 |
+
if *(rb(va::G_DISPLAY_MODE_FLAG) as *const c_char) == 0 {
|
| 832 |
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
| 833 |
}
|
| 834 |
let disp = (*wrapper).display;
|
| 835 |
let gfx_dir = (*wrapper).primary_gfx_dir;
|
| 836 |
DDDisplay::set_layer_color(disp, 1, 0xFE);
|
| 837 |
+
DDDisplay::load_sprite(
|
| 838 |
+
disp,
|
| 839 |
+
1,
|
| 840 |
+
1,
|
| 841 |
+
0,
|
| 842 |
+
gfx_dir,
|
| 843 |
+
rb(va::STR_CDROM_SPR) as *const c_char,
|
| 844 |
+
);
|
| 845 |
DDDisplay::set_layer_visibility(disp, 1, -100);
|
| 846 |
|
| 847 |
// Palette slot range init (raw byte offsets into DisplayBase)
|
|
|
|
| 874 |
// The layer_ctx is used as the output buffer, not a plain stack alloc.
|
| 875 |
let res = gfx_resource_create(
|
| 876 |
(*wrapper).primary_gfx_dir,
|
| 877 |
+
rb(va::STR_COLOURS_IMG) as *const c_char,
|
| 878 |
layer_ctx,
|
| 879 |
);
|
| 880 |
if !res.is_null() {
|
|
|
|
| 951 |
let gfx_dir = (*wrapper).primary_gfx_dir;
|
| 952 |
let out_buf = wa_malloc(0x900);
|
| 953 |
core::ptr::write_bytes(out_buf, 0, 0x900);
|
| 954 |
+
gfx_resource =
|
| 955 |
+
gfx_resource_create(gfx_dir, rb(va::STR_MASKS_IMG) as *const c_char, out_buf);
|
| 956 |
}
|
| 957 |
|
| 958 |
// ββ PCLandscape (alloc 0xB44, stdcall 11 params) ββ
|
|
|
|
| 1057 |
|
| 1058 |
let layer_ctx = DDDisplay::set_active_layer((*ddgame).display, 1);
|
| 1059 |
|
| 1060 |
+
let entry = gfx_dir_find_entry(name_buf.as_ptr().cast(), gfx_dir);
|
| 1061 |
|
| 1062 |
let sprite: *mut u8;
|
| 1063 |
if !entry.is_null() {
|
|
|
|
| 1071 |
sprite = wa_displaygfx_ctor(cached);
|
| 1072 |
} else {
|
| 1073 |
// Fallback: load from file via GfxDir__LoadImage + IMG_Decode
|
| 1074 |
+
sprite = call_gfx_load_and_wrap(gfx_dir, name_buf.as_ptr().cast(), layer_ctx);
|
| 1075 |
}
|
| 1076 |
} else {
|
| 1077 |
// Entry not found β try direct file load
|
| 1078 |
+
sprite = call_gfx_load_and_wrap(gfx_dir, name_buf.as_ptr().cast(), layer_ctx);
|
| 1079 |
}
|
| 1080 |
|
| 1081 |
// Store arrow sprite at DDGame+0x38+i*4
|
|
|
|
| 1253 |
land_layer,
|
| 1254 |
c"back.spr".as_ptr().cast(),
|
| 1255 |
);
|
| 1256 |
+
DDDisplay::load_sprite(disp, 3, 0x26E, 0, land_layer, c"debris.spr".as_ptr());
|
| 1257 |
}
|
| 1258 |
|
| 1259 |
DDDisplay::load_sprite_by_layer(
|
|
|
|
| 1331 |
}
|
| 1332 |
|
| 1333 |
// ββ FUN_00570A90 (second call, conditional) ββ
|
| 1334 |
+
if *(rb(va::G_DISPLAY_MODE_FLAG) as *const c_char) == 0 {
|
| 1335 |
call_usercall_eax(wrapper, FUN_570A90_ADDR);
|
| 1336 |
}
|
| 1337 |
|
|
|
|
| 1470 |
pub const TEAM_BLOCKS: usize = 0x4090;
|
| 1471 |
|
| 1472 |
/// Byte offset from TeamArenaState base back to FullTeamBlock array start.
|
| 1473 |
+
/// `blocks_ptr = (tws_base as *const c_char).sub(ARENA_TO_BLOCKS) as *const FullTeamBlock`
|
| 1474 |
///
|
| 1475 |
/// entry_ptr(0) = DDGame+0x4628 = TEAM_BLOCKS + 0x598.
|
| 1476 |
/// 0x598 = sizeof(FullTeamBlock) + 0x7C = one block + offset into TeamBlockHeader.
|
crates/openwa-core/src/render/gfx_dir.rs
CHANGED
|
@@ -104,8 +104,8 @@ unsafe fn gfx_dir_hash(name: *const u8) -> u32 {
|
|
| 104 |
/// `gfx_dir` must be a valid GfxHandler with initialized hash table at +0x04.
|
| 105 |
/// `name` must be a valid null-terminated C string.
|
| 106 |
#[cfg(target_arch = "x86")]
|
| 107 |
-
pub unsafe fn gfx_dir_find_entry(name: *const
|
| 108 |
-
let mut current_name = name;
|
| 109 |
|
| 110 |
loop {
|
| 111 |
// Copy name to stack buffer (max 0xFF chars + null)
|
|
@@ -335,7 +335,11 @@ pub unsafe fn gfx_dir_load_dir(handler: *mut u8) -> i32 {
|
|
| 335 |
/// # Safety
|
| 336 |
/// All pointers must be valid WA objects.
|
| 337 |
#[cfg(target_arch = "x86")]
|
| 338 |
-
pub unsafe fn gfx_resource_create(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
// 1. Try FindEntry β cached load β DisplayGfx wrap
|
| 340 |
let entry = gfx_dir_find_entry(name, gfx_dir);
|
| 341 |
if !entry.is_null() {
|
|
@@ -382,7 +386,7 @@ pub(crate) unsafe fn call_gfx_find_and_load(
|
|
| 382 |
display_ctx: *mut u8,
|
| 383 |
) -> *mut u8 {
|
| 384 |
let name_ptr = name.as_ptr() as *const u8;
|
| 385 |
-
let entry = gfx_dir_find_entry(name_ptr, gfx_dir);
|
| 386 |
|
| 387 |
if !entry.is_null() {
|
| 388 |
// Try cached load: gfx_dir->vtable[2](entry->field_4)
|
|
@@ -400,7 +404,7 @@ pub(crate) unsafe fn call_gfx_find_and_load(
|
|
| 400 |
}
|
| 401 |
|
| 402 |
// Fallback: load image directly
|
| 403 |
-
call_gfx_load_and_wrap(gfx_dir, name_ptr, display_ctx)
|
| 404 |
}
|
| 405 |
|
| 406 |
/// Helper: load image via GfxDir__LoadImage + wrap as DisplayGfx.
|
|
@@ -408,7 +412,7 @@ pub(crate) unsafe fn call_gfx_find_and_load(
|
|
| 408 |
#[cfg(target_arch = "x86")]
|
| 409 |
pub(crate) unsafe fn call_gfx_load_and_wrap(
|
| 410 |
gfx_dir: *mut u8,
|
| 411 |
-
name: *const
|
| 412 |
display_ctx: *mut u8,
|
| 413 |
) -> *mut u8 {
|
| 414 |
let image = call_gfx_load_image(gfx_dir, name);
|
|
@@ -450,7 +454,10 @@ pub(crate) unsafe fn call_gfx_load_dir(handler: *mut u8, addr: u32) -> i32 {
|
|
| 450 |
|
| 451 |
#[cfg(target_arch = "x86")]
|
| 452 |
#[unsafe(naked)]
|
| 453 |
-
unsafe extern "C" fn call_gfx_load_image(
|
|
|
|
|
|
|
|
|
|
| 454 |
core::arch::naked_asm!(
|
| 455 |
"pushl %esi",
|
| 456 |
"movl 8(%esp), %esi", // ESI = gfx_dir
|
|
|
|
| 104 |
/// `gfx_dir` must be a valid GfxHandler with initialized hash table at +0x04.
|
| 105 |
/// `name` must be a valid null-terminated C string.
|
| 106 |
#[cfg(target_arch = "x86")]
|
| 107 |
+
pub unsafe fn gfx_dir_find_entry(name: *const core::ffi::c_char, gfx_dir: *mut u8) -> *mut u8 {
|
| 108 |
+
let mut current_name = name as *const u8;
|
| 109 |
|
| 110 |
loop {
|
| 111 |
// Copy name to stack buffer (max 0xFF chars + null)
|
|
|
|
| 335 |
/// # Safety
|
| 336 |
/// All pointers must be valid WA objects.
|
| 337 |
#[cfg(target_arch = "x86")]
|
| 338 |
+
pub unsafe fn gfx_resource_create(
|
| 339 |
+
gfx_dir: *mut u8,
|
| 340 |
+
name: *const core::ffi::c_char,
|
| 341 |
+
output: *mut u8,
|
| 342 |
+
) -> *mut u8 {
|
| 343 |
// 1. Try FindEntry β cached load β DisplayGfx wrap
|
| 344 |
let entry = gfx_dir_find_entry(name, gfx_dir);
|
| 345 |
if !entry.is_null() {
|
|
|
|
| 386 |
display_ctx: *mut u8,
|
| 387 |
) -> *mut u8 {
|
| 388 |
let name_ptr = name.as_ptr() as *const u8;
|
| 389 |
+
let entry = gfx_dir_find_entry(name_ptr.cast(), gfx_dir);
|
| 390 |
|
| 391 |
if !entry.is_null() {
|
| 392 |
// Try cached load: gfx_dir->vtable[2](entry->field_4)
|
|
|
|
| 404 |
}
|
| 405 |
|
| 406 |
// Fallback: load image directly
|
| 407 |
+
call_gfx_load_and_wrap(gfx_dir, name_ptr.cast(), display_ctx)
|
| 408 |
}
|
| 409 |
|
| 410 |
/// Helper: load image via GfxDir__LoadImage + wrap as DisplayGfx.
|
|
|
|
| 412 |
#[cfg(target_arch = "x86")]
|
| 413 |
pub(crate) unsafe fn call_gfx_load_and_wrap(
|
| 414 |
gfx_dir: *mut u8,
|
| 415 |
+
name: *const core::ffi::c_char,
|
| 416 |
display_ctx: *mut u8,
|
| 417 |
) -> *mut u8 {
|
| 418 |
let image = call_gfx_load_image(gfx_dir, name);
|
|
|
|
| 454 |
|
| 455 |
#[cfg(target_arch = "x86")]
|
| 456 |
#[unsafe(naked)]
|
| 457 |
+
unsafe extern "C" fn call_gfx_load_image(
|
| 458 |
+
_gfx_dir: *mut u8,
|
| 459 |
+
_name: *const core::ffi::c_char,
|
| 460 |
+
) -> *mut u8 {
|
| 461 |
core::arch::naked_asm!(
|
| 462 |
"pushl %esi",
|
| 463 |
"movl 8(%esp), %esi", // ESI = gfx_dir
|
crates/openwa-wormkit/src/replacements/ddgame_init.rs
CHANGED
|
@@ -12,6 +12,7 @@
|
|
| 12 |
//! - `FUN_570E20` (display layer init): usercall(ESI=wrapper), plain RET
|
| 13 |
|
| 14 |
use crate::hook;
|
|
|
|
| 15 |
use openwa_core::address::va;
|
| 16 |
use openwa_core::engine::ddgame::{
|
| 17 |
bit_grid_init, ddgame_init_fields, ddgame_init_render_indices, display_layer_color_init,
|
|
@@ -84,8 +85,9 @@ extern "cdecl" fn impl_display_layer_init(wrapper: u32) -> u32 {
|
|
| 84 |
// βββ GfxResource__Create_Maybe (0x4F6300) βββββββββββββββββββββββββββββββββββ
|
| 85 |
|
| 86 |
extern "cdecl" fn impl_gfx_resource_create(gfx_dir: u32, name: u32, output: u32) -> u32 {
|
| 87 |
-
let result =
|
| 88 |
-
|
|
|
|
| 89 |
result as u32
|
| 90 |
}
|
| 91 |
|
|
@@ -118,7 +120,7 @@ hook::usercall_trampoline!(
|
|
| 118 |
static FIND_ENTRY_LOG_COUNT: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
|
| 119 |
|
| 120 |
extern "cdecl" fn impl_find_entry(name: u32, gfx_dir: u32) -> u32 {
|
| 121 |
-
let result = unsafe { gfx_dir_find_entry(name as *const
|
| 122 |
|
| 123 |
// Log first 20 lookups for debugging
|
| 124 |
let count = FIND_ENTRY_LOG_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
|
|
|
| 12 |
//! - `FUN_570E20` (display layer init): usercall(ESI=wrapper), plain RET
|
| 13 |
|
| 14 |
use crate::hook;
|
| 15 |
+
use core::ffi::c_char;
|
| 16 |
use openwa_core::address::va;
|
| 17 |
use openwa_core::engine::ddgame::{
|
| 18 |
bit_grid_init, ddgame_init_fields, ddgame_init_render_indices, display_layer_color_init,
|
|
|
|
| 85 |
// βββ GfxResource__Create_Maybe (0x4F6300) βββββββββββββββββββββββββββββββββββ
|
| 86 |
|
| 87 |
extern "cdecl" fn impl_gfx_resource_create(gfx_dir: u32, name: u32, output: u32) -> u32 {
|
| 88 |
+
let result = unsafe {
|
| 89 |
+
gfx_resource_create(gfx_dir as *mut u8, name as *const c_char, output as *mut u8)
|
| 90 |
+
};
|
| 91 |
result as u32
|
| 92 |
}
|
| 93 |
|
|
|
|
| 120 |
static FIND_ENTRY_LOG_COUNT: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
|
| 121 |
|
| 122 |
extern "cdecl" fn impl_find_entry(name: u32, gfx_dir: u32) -> u32 {
|
| 123 |
+
let result = unsafe { gfx_dir_find_entry(name as *const c_char, gfx_dir as *mut u8) };
|
| 124 |
|
| 125 |
// Log first 20 lookups for debugging
|
| 126 |
let count = FIND_ENTRY_LOG_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|