paavohtl Claude Opus 4.6 (1M context) commited on
Commit
10ece8a
·
1 Parent(s): 12a0785

fix: port shadow LUT remap to Rust, fixing Indian Nuclear Test crash

Browse files

The SHADOW_CLEAR path in blit_sprite called BitGrid__RemapPixelsThroughLut
(0x4F6590) as a plain cdecl function, but it's actually a usercall that
expects EAX = BitGrid pointer with one stdcall stack param (RET 0x4).
EAX was never set, so the function read a garbage BitGrid pointer and
wrote to invalid memory.

Replaced the broken asm bridge with a pure Rust port of the function —
a simple double loop remapping 8bpp pixels through a 256-byte LUT.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/render/display/blit_sprite.rs CHANGED
@@ -208,10 +208,9 @@ pub unsafe fn blit_sprite(
208
  let saved = *table_byte;
209
  *table_byte = 0;
210
 
211
- // Call BitGrid__ClearColumn_Maybe (0x4F6590) — clears shadow channel
212
- let clear_fn: unsafe extern "cdecl" fn(*mut u8) =
213
- core::mem::transmute(rb(0x004F6590) as usize);
214
- clear_fn(table_byte as *mut u8);
215
 
216
  *table_byte = saved;
217
 
@@ -452,3 +451,35 @@ pub unsafe fn draw_scaled_sprite(
452
  DrawScaledSpriteResult::Handled => {}
453
  }
454
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  let saved = *table_byte;
209
  *table_byte = 0;
210
 
211
+ // BitGrid__RemapPixelsThroughLut (0x4F6590) — remaps layer_2 pixels
212
+ // through the color table.
213
+ remap_pixels_through_shadow_lut(layer2, table_byte as *mut u8);
 
214
 
215
  *table_byte = saved;
216
 
 
451
  DrawScaledSpriteResult::Handled => {}
452
  }
453
  }
454
+
455
+ /// Pure Rust port of `BitGrid__RemapPixelsThroughLut` (0x4F6590).
456
+ ///
457
+ /// Remaps every pixel in an 8bpp BitGrid through a 256-byte LUT.
458
+ /// The inner loop is unrolled 4x, matching the original.
459
+ unsafe fn remap_pixels_through_shadow_lut(bitgrid: *mut DisplayBitGrid, color_table: *const u8) {
460
+ let grid = &*bitgrid;
461
+ if grid.cells_per_unit != 8 {
462
+ return;
463
+ }
464
+
465
+ let data = grid.data;
466
+ let stride = grid.row_stride as usize;
467
+ let height = grid.height as usize;
468
+ // The original passes stride/4 as the inner loop count (4 pixels per iteration).
469
+ let quads_per_row = stride / 4;
470
+
471
+ let lut = core::slice::from_raw_parts(color_table, 256);
472
+ let mut row_ptr = data;
473
+
474
+ for _ in 0..height {
475
+ let mut p = row_ptr;
476
+ for _ in 0..quads_per_row {
477
+ *p = lut[*p as usize];
478
+ *p.add(1) = lut[*p.add(1) as usize];
479
+ *p.add(2) = lut[*p.add(2) as usize];
480
+ *p.add(3) = lut[*p.add(3) as usize];
481
+ p = p.add(4);
482
+ }
483
+ row_ptr = row_ptr.add(stride);
484
+ }
485
+ }