refactor: use typed Sprite/PaletteContext/SpriteFrame in sprite hook impls
Browse filesReplace raw u32 params and pointer arithmetic with typed struct pointers
in construct_sprite_impl and process_sprite_impl. Type Sprite's
frame_meta_ptr and secondary_frame_ptr as *mut SpriteFrame.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/render/sprite.rs
CHANGED
|
@@ -788,9 +788,9 @@ pub struct Sprite {
|
|
| 788 |
/// 0x24: Is-scaled flag (1 if scaling active, 0 otherwise)
|
| 789 |
pub is_scaled: u32,
|
| 790 |
/// 0x28: Pointer to SpriteFrame array (frame_count entries)
|
| 791 |
-
pub frame_meta_ptr: *mut
|
| 792 |
/// 0x2C: Secondary frame table pointer (present when header_flags & 0x4000)
|
| 793 |
-
pub secondary_frame_ptr: *mut
|
| 794 |
/// 0x30: Secondary frame count
|
| 795 |
pub secondary_frame_count: u16,
|
| 796 |
/// 0x32: Padding
|
|
|
|
| 788 |
/// 0x24: Is-scaled flag (1 if scaling active, 0 otherwise)
|
| 789 |
pub is_scaled: u32,
|
| 790 |
/// 0x28: Pointer to SpriteFrame array (frame_count entries)
|
| 791 |
+
pub frame_meta_ptr: *mut SpriteFrame,
|
| 792 |
/// 0x2C: Secondary frame table pointer (present when header_flags & 0x4000)
|
| 793 |
+
pub secondary_frame_ptr: *mut SpriteFrame,
|
| 794 |
/// 0x30: Secondary frame count
|
| 795 |
pub secondary_frame_count: u16,
|
| 796 |
/// 0x32: Padding
|
crates/openwa-wormkit/src/replacements/sprite.rs
CHANGED
|
@@ -6,6 +6,8 @@
|
|
| 6 |
|
| 7 |
use openwa_core::address::va;
|
| 8 |
use openwa_core::rebase::rb;
|
|
|
|
|
|
|
| 9 |
|
| 10 |
use crate::hook::{self, usercall_trampoline};
|
| 11 |
|
|
@@ -16,21 +18,17 @@ use crate::hook::{self, usercall_trampoline};
|
|
| 16 |
usercall_trampoline!(fn trampoline_construct_sprite; impl_fn = construct_sprite_impl;
|
| 17 |
regs = [eax, ecx]);
|
| 18 |
|
| 19 |
-
unsafe extern "cdecl" fn construct_sprite_impl(sprite:
|
| 20 |
-
let p = sprite as *mut u8;
|
| 21 |
-
|
| 22 |
// Zero the entire 0x70-byte struct first
|
| 23 |
-
core::ptr::write_bytes(
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
// DisplayGfx
|
| 30 |
-
|
| 31 |
-
// _unknown_38 = 1 (+0x38)
|
| 32 |
*(p.add(0x38) as *mut u32) = 1;
|
| 33 |
-
// _unknown_40 = 8 (+0x40)
|
| 34 |
*(p.add(0x40) as *mut u32) = 8;
|
| 35 |
}
|
| 36 |
|
|
@@ -55,23 +53,23 @@ unsafe extern "cdecl" fn palette_map_color_impl(palette_ctx: u32, rgb: u32) -> u
|
|
| 55 |
openwa_core::render::palette::palette_map_color(ctx, rgb)
|
| 56 |
}
|
| 57 |
|
| 58 |
-
unsafe extern "cdecl" fn process_sprite_impl(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
use openwa_core::render::spr::parse_spr_header;
|
| 60 |
|
| 61 |
-
|
| 62 |
-
let
|
| 63 |
-
|
| 64 |
-
// We need the data as a slice. Use data_size from header to determine length.
|
| 65 |
-
// data_size is at raw_data + 4.
|
| 66 |
-
let data_size = *(data_ptr.add(4) as *const u32);
|
| 67 |
// Total buffer: data_size covers from offset +4 onward, so total = data_size + 4.
|
| 68 |
let data_len = (data_size + 4) as usize;
|
| 69 |
-
let data = core::slice::from_raw_parts(
|
| 70 |
|
| 71 |
let hdr = match parse_spr_header(data) {
|
| 72 |
Ok(h) => h,
|
| 73 |
Err(e) => {
|
| 74 |
-
// This should never happen with valid WA game data
|
| 75 |
panic!("ProcessSprite: failed to parse .spr data: {}", e);
|
| 76 |
}
|
| 77 |
};
|
|
@@ -81,18 +79,18 @@ unsafe extern "cdecl" fn process_sprite_impl(sprite: u32, palette_ctx: u32, raw_
|
|
| 81 |
*g_data_bytes = (*g_data_bytes).wrapping_add(data_size);
|
| 82 |
|
| 83 |
// --- Store raw_frame_header_ptr (points to header_flags in raw buffer) ---
|
| 84 |
-
|
| 85 |
|
| 86 |
// --- Store header_flags ---
|
| 87 |
-
|
| 88 |
|
| 89 |
// --- Build palette lookup table IN PLACE ---
|
| 90 |
// palette_data_ptr points to raw_data + 0x0A (the palette_entry_count field).
|
| 91 |
// WA overwrites this region with a 1-indexed palette index lookup table:
|
| 92 |
// [0] = 0 (transparent)
|
| 93 |
// [1..N] = PaletteContext__MapColor(rgb) for each entry
|
| 94 |
-
let palette_base =
|
| 95 |
-
|
| 96 |
|
| 97 |
// Update global counter: palette bytes
|
| 98 |
let g_palette = rb(va::G_SPRITE_PALETTE_BYTES) as *mut u32;
|
|
@@ -102,80 +100,74 @@ unsafe extern "cdecl" fn process_sprite_impl(sprite: u32, palette_ctx: u32, raw_
|
|
| 102 |
*palette_base = 0;
|
| 103 |
|
| 104 |
// Map each RGB entry to display palette index
|
| 105 |
-
let rgb_start =
|
| 106 |
-
let ctx = palette_ctx as *mut openwa_core::render::palette::PaletteContext;
|
| 107 |
for i in 0..hdr.palette_count as usize {
|
| 108 |
// Read 4 bytes (3 RGB + 1 from next entry, matching WA behavior)
|
| 109 |
let rgb_val = *(rgb_start.add(i * 3) as *const u32);
|
| 110 |
-
let mapped =
|
| 111 |
*palette_base.add(1 + i) = mapped as u8;
|
| 112 |
}
|
| 113 |
|
| 114 |
// --- Secondary frame table (if header_flags & 0x4000) ---
|
| 115 |
let has_secondary = hdr.header_flags & 0x4000 != 0;
|
| 116 |
if has_secondary {
|
| 117 |
-
|
| 118 |
-
|
|
|
|
| 119 |
}
|
| 120 |
|
| 121 |
// --- Main frame header fields ---
|
| 122 |
// Copy unknown_08 + fps as a single u32 (matching WA's 4-byte copy)
|
| 123 |
let frame_header_ptr = if has_secondary {
|
| 124 |
-
|
| 125 |
-
data_ptr.add(hdr.secondary_frame_offset + hdr.secondary_frame_count as usize * 12)
|
| 126 |
} else {
|
| 127 |
-
|
| 128 |
};
|
| 129 |
|
| 130 |
// WA copies 4 bytes at once: *(u32*)(sprite+8) = *(u32*)(frame_header)
|
|
|
|
| 131 |
*(p.add(0x08) as *mut u32) = *(frame_header_ptr as *const u32);
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
|
| 138 |
// --- Scale fields ---
|
| 139 |
if hdr.is_scaled {
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
} else {
|
| 144 |
-
|
| 145 |
}
|
| 146 |
|
| 147 |
// --- Frame metadata and bitmap pointers ---
|
| 148 |
-
let frame_meta_ptr =
|
| 149 |
-
let bitmap_ptr =
|
| 150 |
-
|
| 151 |
-
|
| 152 |
|
| 153 |
// --- Bitmap palette remapping (only when NO secondary frames) ---
|
| 154 |
if !has_secondary {
|
| 155 |
// Remap every bitmap byte: pixel = lookup_table[pixel]
|
| 156 |
-
let
|
| 157 |
-
let bitmap_byte_count_raw = (data_size as usize + 4).saturating_sub(bitmap_start_relative);
|
| 158 |
let dword_count = (bitmap_byte_count_raw + 3) / 4;
|
| 159 |
let remap_byte_count = dword_count * 4;
|
| 160 |
|
| 161 |
-
let bmp = bitmap_ptr as *mut u8;
|
| 162 |
for i in 0..remap_byte_count {
|
| 163 |
-
let idx = *
|
| 164 |
-
*
|
| 165 |
}
|
| 166 |
}
|
| 167 |
|
| 168 |
// --- Update global counters: pixel area and frame count ---
|
| 169 |
if hdr.frame_count > 0 {
|
| 170 |
let g_pixel_area = rb(va::G_SPRITE_PIXEL_AREA) as *mut u32;
|
| 171 |
-
let frames_ptr = frame_meta_ptr as *const [u8; 12];
|
| 172 |
for i in 0..hdr.frame_count as usize {
|
| 173 |
-
let frame = &*
|
| 174 |
-
let
|
| 175 |
-
|
| 176 |
-
let end_x = i16::from_le_bytes([frame[8], frame[9]]);
|
| 177 |
-
let end_y = i16::from_le_bytes([frame[10], frame[11]]);
|
| 178 |
-
let area = (end_x as i32 - start_x as i32) * (end_y as i32 - start_y as i32);
|
| 179 |
*g_pixel_area = (*g_pixel_area).wrapping_add(area as u32);
|
| 180 |
}
|
| 181 |
}
|
|
|
|
| 6 |
|
| 7 |
use openwa_core::address::va;
|
| 8 |
use openwa_core::rebase::rb;
|
| 9 |
+
use openwa_core::render::palette::PaletteContext;
|
| 10 |
+
use openwa_core::render::sprite::{Sprite, SpriteFrame};
|
| 11 |
|
| 12 |
use crate::hook::{self, usercall_trampoline};
|
| 13 |
|
|
|
|
| 18 |
usercall_trampoline!(fn trampoline_construct_sprite; impl_fn = construct_sprite_impl;
|
| 19 |
regs = [eax, ecx]);
|
| 20 |
|
| 21 |
+
unsafe extern "cdecl" fn construct_sprite_impl(sprite: *mut Sprite, context: *mut u8) {
|
|
|
|
|
|
|
| 22 |
// Zero the entire 0x70-byte struct first
|
| 23 |
+
core::ptr::write_bytes(sprite as *mut u8, 0, core::mem::size_of::<Sprite>());
|
| 24 |
+
|
| 25 |
+
(*sprite).vtable = rb(va::SPRITE_VTABLE) as *mut u8;
|
| 26 |
+
(*sprite).context_ptr = context;
|
| 27 |
+
(*sprite).display_gfx = rb(va::DISPLAYGFX_VTABLE) as *mut u8;
|
| 28 |
+
|
| 29 |
+
// DisplayGfx sub-object fields (within _unknown_38)
|
| 30 |
+
let p = sprite as *mut u8;
|
|
|
|
| 31 |
*(p.add(0x38) as *mut u32) = 1;
|
|
|
|
| 32 |
*(p.add(0x40) as *mut u32) = 8;
|
| 33 |
}
|
| 34 |
|
|
|
|
| 53 |
openwa_core::render::palette::palette_map_color(ctx, rgb)
|
| 54 |
}
|
| 55 |
|
| 56 |
+
unsafe extern "cdecl" fn process_sprite_impl(
|
| 57 |
+
sprite: *mut Sprite,
|
| 58 |
+
palette_ctx: *mut PaletteContext,
|
| 59 |
+
raw_data: *const u8,
|
| 60 |
+
) -> u32 {
|
| 61 |
+
use openwa_core::render::palette::palette_map_color;
|
| 62 |
use openwa_core::render::spr::parse_spr_header;
|
| 63 |
|
| 64 |
+
// We need the data as a slice. data_size is at raw_data + 4.
|
| 65 |
+
let data_size = *(raw_data.add(4) as *const u32);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
// Total buffer: data_size covers from offset +4 onward, so total = data_size + 4.
|
| 67 |
let data_len = (data_size + 4) as usize;
|
| 68 |
+
let data = core::slice::from_raw_parts(raw_data, data_len);
|
| 69 |
|
| 70 |
let hdr = match parse_spr_header(data) {
|
| 71 |
Ok(h) => h,
|
| 72 |
Err(e) => {
|
|
|
|
| 73 |
panic!("ProcessSprite: failed to parse .spr data: {}", e);
|
| 74 |
}
|
| 75 |
};
|
|
|
|
| 79 |
*g_data_bytes = (*g_data_bytes).wrapping_add(data_size);
|
| 80 |
|
| 81 |
// --- Store raw_frame_header_ptr (points to header_flags in raw buffer) ---
|
| 82 |
+
(*sprite).raw_frame_header_ptr = raw_data.add(8) as *mut u8;
|
| 83 |
|
| 84 |
// --- Store header_flags ---
|
| 85 |
+
(*sprite).header_flags = hdr.header_flags;
|
| 86 |
|
| 87 |
// --- Build palette lookup table IN PLACE ---
|
| 88 |
// palette_data_ptr points to raw_data + 0x0A (the palette_entry_count field).
|
| 89 |
// WA overwrites this region with a 1-indexed palette index lookup table:
|
| 90 |
// [0] = 0 (transparent)
|
| 91 |
// [1..N] = PaletteContext__MapColor(rgb) for each entry
|
| 92 |
+
let palette_base = raw_data.add(0x0A) as *mut u8;
|
| 93 |
+
(*sprite).palette_data_ptr = palette_base;
|
| 94 |
|
| 95 |
// Update global counter: palette bytes
|
| 96 |
let g_palette = rb(va::G_SPRITE_PALETTE_BYTES) as *mut u32;
|
|
|
|
| 100 |
*palette_base = 0;
|
| 101 |
|
| 102 |
// Map each RGB entry to display palette index
|
| 103 |
+
let rgb_start = raw_data.add(hdr.palette_offset);
|
|
|
|
| 104 |
for i in 0..hdr.palette_count as usize {
|
| 105 |
// Read 4 bytes (3 RGB + 1 from next entry, matching WA behavior)
|
| 106 |
let rgb_val = *(rgb_start.add(i * 3) as *const u32);
|
| 107 |
+
let mapped = palette_map_color(palette_ctx, rgb_val);
|
| 108 |
*palette_base.add(1 + i) = mapped as u8;
|
| 109 |
}
|
| 110 |
|
| 111 |
// --- Secondary frame table (if header_flags & 0x4000) ---
|
| 112 |
let has_secondary = hdr.header_flags & 0x4000 != 0;
|
| 113 |
if has_secondary {
|
| 114 |
+
(*sprite).secondary_frame_count = hdr.secondary_frame_count;
|
| 115 |
+
(*sprite).secondary_frame_ptr =
|
| 116 |
+
raw_data.add(hdr.secondary_frame_offset) as *mut SpriteFrame;
|
| 117 |
}
|
| 118 |
|
| 119 |
// --- Main frame header fields ---
|
| 120 |
// Copy unknown_08 + fps as a single u32 (matching WA's 4-byte copy)
|
| 121 |
let frame_header_ptr = if has_secondary {
|
| 122 |
+
raw_data.add(hdr.secondary_frame_offset + hdr.secondary_frame_count as usize * 12)
|
|
|
|
| 123 |
} else {
|
| 124 |
+
raw_data.add(hdr.palette_offset + hdr.palette_count as usize * 3)
|
| 125 |
};
|
| 126 |
|
| 127 |
// WA copies 4 bytes at once: *(u32*)(sprite+8) = *(u32*)(frame_header)
|
| 128 |
+
let p = sprite as *mut u8;
|
| 129 |
*(p.add(0x08) as *mut u32) = *(frame_header_ptr as *const u32);
|
| 130 |
+
(*sprite).flags = hdr.flags;
|
| 131 |
+
(*sprite).width = hdr.width;
|
| 132 |
+
(*sprite).height = hdr.height;
|
| 133 |
+
(*sprite).frame_count = hdr.frame_count;
|
| 134 |
+
(*sprite).max_frames = hdr.max_frames;
|
| 135 |
|
| 136 |
// --- Scale fields ---
|
| 137 |
if hdr.is_scaled {
|
| 138 |
+
(*sprite).scale_x = hdr.scale_x;
|
| 139 |
+
(*sprite).scale_y = hdr.scale_y;
|
| 140 |
+
(*sprite).is_scaled = 1;
|
| 141 |
} else {
|
| 142 |
+
(*sprite).is_scaled = 0;
|
| 143 |
}
|
| 144 |
|
| 145 |
// --- Frame metadata and bitmap pointers ---
|
| 146 |
+
let frame_meta_ptr = raw_data.add(hdr.frame_meta_offset) as *mut SpriteFrame;
|
| 147 |
+
let bitmap_ptr = raw_data.add(hdr.bitmap_offset) as *mut u8;
|
| 148 |
+
(*sprite).frame_meta_ptr = frame_meta_ptr;
|
| 149 |
+
(*sprite).bitmap_data_ptr = bitmap_ptr;
|
| 150 |
|
| 151 |
// --- Bitmap palette remapping (only when NO secondary frames) ---
|
| 152 |
if !has_secondary {
|
| 153 |
// Remap every bitmap byte: pixel = lookup_table[pixel]
|
| 154 |
+
let bitmap_byte_count_raw = (data_size as usize + 4).saturating_sub(hdr.bitmap_offset);
|
|
|
|
| 155 |
let dword_count = (bitmap_byte_count_raw + 3) / 4;
|
| 156 |
let remap_byte_count = dword_count * 4;
|
| 157 |
|
|
|
|
| 158 |
for i in 0..remap_byte_count {
|
| 159 |
+
let idx = *bitmap_ptr.add(i) as usize;
|
| 160 |
+
*bitmap_ptr.add(i) = *palette_base.add(idx);
|
| 161 |
}
|
| 162 |
}
|
| 163 |
|
| 164 |
// --- Update global counters: pixel area and frame count ---
|
| 165 |
if hdr.frame_count > 0 {
|
| 166 |
let g_pixel_area = rb(va::G_SPRITE_PIXEL_AREA) as *mut u32;
|
|
|
|
| 167 |
for i in 0..hdr.frame_count as usize {
|
| 168 |
+
let frame = &*frame_meta_ptr.add(i);
|
| 169 |
+
let area = (frame.end_x as i32 - frame.start_x as i32)
|
| 170 |
+
* (frame.end_y as i32 - frame.start_y as i32);
|
|
|
|
|
|
|
|
|
|
| 171 |
*g_pixel_area = (*g_pixel_area).wrapping_add(area as u32);
|
| 172 |
}
|
| 173 |
}
|