paavohtl Claude Opus 4.6 (1M context) commited on
Commit
14c0aff
·
1 Parent(s): 6124d55

refactor: use typed Sprite/PaletteContext/SpriteFrame in sprite hook impls

Browse files

Replace 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 u8,
792
  /// 0x2C: Secondary frame table pointer (present when header_flags & 0x4000)
793
- pub secondary_frame_ptr: *mut u8,
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: u32, context: u32) {
20
- let p = sprite as *mut u8;
21
-
22
  // Zero the entire 0x70-byte struct first
23
- core::ptr::write_bytes(p, 0, 0x70);
24
-
25
- // Vtable
26
- *(p as *mut u32) = rb(va::SPRITE_VTABLE);
27
- // Context pointer (+0x04)
28
- *(p.add(0x04) as *mut u32) = context;
29
- // DisplayGfx vtable (+0x34)
30
- *(p.add(0x34) as *mut u32) = rb(va::DISPLAYGFX_VTABLE);
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(sprite: u32, palette_ctx: u32, raw_data: u32) -> u32 {
 
 
 
 
 
59
  use openwa_core::render::spr::parse_spr_header;
60
 
61
- let p = sprite as *mut u8;
62
- let data_ptr = raw_data as *const u8;
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(data_ptr, data_len);
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
- *(p.add(0x60) as *mut *const u8) = data_ptr.add(8);
85
 
86
  // --- Store header_flags ---
87
- *(p.add(0x14) as *mut u16) = hdr.header_flags;
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 = data_ptr.add(0x0A) as *mut u8;
95
- *(p.add(0x68) as *mut *mut u8) = palette_base;
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 = data_ptr.add(hdr.palette_offset);
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 = openwa_core::render::palette::palette_map_color(ctx, rgb_val);
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
- *(p.add(0x30) as *mut u16) = hdr.secondary_frame_count;
118
- *(p.add(0x2C) as *mut *const u8) = data_ptr.add(hdr.secondary_frame_offset);
 
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
- // After secondary frames
125
- data_ptr.add(hdr.secondary_frame_offset + hdr.secondary_frame_count as usize * 12)
126
  } else {
127
- data_ptr.add(hdr.palette_offset + hdr.palette_count as usize * 3)
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
- *(p.add(0x10) as *mut u16) = hdr.flags;
133
- *(p.add(0x0C) as *mut u16) = hdr.width;
134
- *(p.add(0x0E) as *mut u16) = hdr.height;
135
- *(p.add(0x12) as *mut u16) = hdr.frame_count;
136
- *(p.add(0x16) as *mut u16) = hdr.max_frames;
137
 
138
  // --- Scale fields ---
139
  if hdr.is_scaled {
140
- *(p.add(0x1C) as *mut u32) = hdr.scale_x;
141
- *(p.add(0x20) as *mut u32) = hdr.scale_y;
142
- *(p.add(0x24) as *mut u32) = 1; // is_scaled
143
  } else {
144
- *(p.add(0x24) as *mut u32) = 0;
145
  }
146
 
147
  // --- Frame metadata and bitmap pointers ---
148
- let frame_meta_ptr = data_ptr.add(hdr.frame_meta_offset);
149
- let bitmap_ptr = data_ptr.add(hdr.bitmap_offset);
150
- *(p.add(0x28) as *mut *const u8) = frame_meta_ptr;
151
- *(p.add(0x64) as *mut *const u8) = bitmap_ptr;
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 bitmap_start_relative = hdr.bitmap_offset;
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 = *bmp.add(i) as usize;
164
- *bmp.add(i) = *palette_base.add(idx);
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 = &*frames_ptr.add(i);
174
- let start_x = i16::from_le_bytes([frame[4], frame[5]]);
175
- let start_y = i16::from_le_bytes([frame[6], frame[7]]);
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
  }