paavohtl Claude Opus 4.6 (1M context) commited on
Commit
d350890
·
1 Parent(s): dae9128

fix: resolve all clippy warnings across core, wormkit, and test-runner

Browse files

Suppress crate-level lints that aren't worth fixing in an FFI-heavy project:
- missing_safety_doc (71 instances — nearly all functions are unsafe FFI)
- too_many_arguments (FFI signatures can't be simplified)
- missing_transmute_annotations (verbose for function pointer casts)
- manual_clamp (FFI init patterns differ from Rust clamp semantics)
- type_complexity (common in FFI function pointer types)
- empty_line_after_doc_comments, doc_lazy_continuation (formatting churn)

Fix structural warnings:
- Use .flatten() instead of if-let on iterator (dssound.rs)
- Use iter_mut() instead of index-based loop (gfx_dir.rs)
- Replace mem::forget with ManuallyDrop (wa_alloc.rs)
- Add CStringRef::is_empty() (mfc.rs)
- Use Range::contains() via cargo clippy --fix (multiple files)
- Escape doc comment link references (game_state_init.rs)

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

crates/openwa-core/src/audio/dssound.rs CHANGED
@@ -873,12 +873,10 @@ pub unsafe extern "thiscall" fn load_wav(
873
  }
874
  };
875
  let mut offset = 0usize;
876
- for sample in reader.samples::<i16>() {
877
- if let Ok(s) = sample {
878
- if offset + 2 <= dest.len() {
879
- dest[offset..offset + 2].copy_from_slice(&s.to_le_bytes());
880
- offset += 2;
881
- }
882
  }
883
  }
884
  }
@@ -891,13 +889,11 @@ pub unsafe extern "thiscall" fn load_wav(
891
  }
892
  };
893
  let mut offset = 0usize;
894
- for sample in reader.samples::<i16>() {
895
- if let Ok(s) = sample {
896
- if offset < dest.len() {
897
- // 8-bit WAV is unsigned (0-255, center at 128)
898
- dest[offset] = (s + 128) as u8;
899
- offset += 1;
900
- }
901
  }
902
  }
903
  }
 
873
  }
874
  };
875
  let mut offset = 0usize;
876
+ for s in reader.samples::<i16>().flatten() {
877
+ if offset + 2 <= dest.len() {
878
+ dest[offset..offset + 2].copy_from_slice(&s.to_le_bytes());
879
+ offset += 2;
 
 
880
  }
881
  }
882
  }
 
889
  }
890
  };
891
  let mut offset = 0usize;
892
+ for s in reader.samples::<i16>().flatten() {
893
+ if offset < dest.len() {
894
+ // 8-bit WAV is unsigned (0-255, center at 128)
895
+ dest[offset] = (s + 128) as u8;
896
+ offset += 1;
 
 
897
  }
898
  }
899
  }
crates/openwa-core/src/audio/sound.rs CHANGED
@@ -141,7 +141,7 @@ impl TryFrom<u32> for SoundId {
141
  type Error = u32;
142
 
143
  fn try_from(value: u32) -> Result<Self, Self::Error> {
144
- if value >= Self::MIN && value <= Self::MAX {
145
  Ok(unsafe { core::mem::transmute(value) })
146
  } else {
147
  Err(value)
 
141
  type Error = u32;
142
 
143
  fn try_from(value: u32) -> Result<Self, Self::Error> {
144
+ if (Self::MIN..=Self::MAX).contains(&value) {
145
  Ok(unsafe { core::mem::transmute(value) })
146
  } else {
147
  Err(value)
crates/openwa-core/src/engine/game_state_init.rs CHANGED
@@ -32,9 +32,9 @@ pub unsafe fn sprite_gfx_table_init(base: *mut u8, count: u32) {
32
  ///
33
  /// Allocates a zero-filled buffer of `capacity` bytes (aligned + 0x20 header),
34
  /// then initializes the ring buffer struct (7 DWORDs):
35
- /// - [0]: data pointer
36
- /// - [1]: capacity
37
- /// - [2]-[6]: zeroed (head, tail, count, etc.)
38
  pub unsafe fn ring_buffer_init(struct_ptr: *mut u8, capacity: u32) {
39
  let alloc_size = ((capacity + 3) & !3) + 0x20;
40
  let data = wa_malloc(alloc_size);
@@ -376,11 +376,10 @@ pub unsafe fn check_weapon_avail(ddgame: *mut DDGame, weapon_index: u32) -> i32
376
  }
377
  }
378
  w if w == Weapon::DoubleTurnTime as u32 => {
379
- if game_version > 0xD1 {
380
- if (*gi).double_turn_time_threshold > 0x7FFF {
381
  return 0;
382
  }
383
- }
384
  }
385
  _ => {}
386
  }
 
32
  ///
33
  /// Allocates a zero-filled buffer of `capacity` bytes (aligned + 0x20 header),
34
  /// then initializes the ring buffer struct (7 DWORDs):
35
+ /// - `[0]`: data pointer
36
+ /// - `[1]`: capacity
37
+ /// - `[2]`-`[6]`: zeroed (head, tail, count, etc.)
38
  pub unsafe fn ring_buffer_init(struct_ptr: *mut u8, capacity: u32) {
39
  let alloc_size = ((capacity + 3) & !3) + 0x20;
40
  let data = wa_malloc(alloc_size);
 
376
  }
377
  }
378
  w if w == Weapon::DoubleTurnTime as u32 => {
379
+ if game_version > 0xD1
380
+ && (*gi).double_turn_time_threshold > 0x7FFF {
381
  return 0;
382
  }
 
383
  }
384
  _ => {}
385
  }
crates/openwa-core/src/game/scheme.rs CHANGED
@@ -147,7 +147,7 @@ impl SchemeFile {
147
  // short V3 files get defaults for the missing tail. We replicate this by
148
  // accepting any size between V2 (0x124) and V3 (0x192) and padding.
149
  if version == SchemeVersion::V3 {
150
- if actual_payload < SCHEME_PAYLOAD_V2 || actual_payload > SCHEME_PAYLOAD_V3 {
151
  return Err(SchemeError::PayloadMismatch {
152
  expected: expected_payload,
153
  got: actual_payload,
 
147
  // short V3 files get defaults for the missing tail. We replicate this by
148
  // accepting any size between V2 (0x124) and V3 (0x192) and padding.
149
  if version == SchemeVersion::V3 {
150
+ if !(SCHEME_PAYLOAD_V2..=SCHEME_PAYLOAD_V3).contains(&actual_payload) {
151
  return Err(SchemeError::PayloadMismatch {
152
  expected: expected_payload,
153
  got: actual_payload,
crates/openwa-core/src/lib.rs CHANGED
@@ -1,4 +1,17 @@
1
  #![allow(non_snake_case)]
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  #[macro_use]
4
  pub mod macros;
 
1
  #![allow(non_snake_case)]
2
+ #![allow(clippy::missing_safety_doc)]
3
+ #![allow(clippy::too_many_arguments)]
4
+ // FFI code frequently casts pointers for clarity even when redundant
5
+ #![allow(clippy::unnecessary_cast)]
6
+ // Transmute annotations are verbose for FFI function pointer casts
7
+ #![allow(clippy::missing_transmute_annotations)]
8
+ // Clamp-like patterns in FFI init code often have subtly different semantics
9
+ #![allow(clippy::manual_clamp)]
10
+ // Complex types are common in FFI function pointer signatures
11
+ #![allow(clippy::type_complexity)]
12
+ // Doc formatting lints — not worth the churn
13
+ #![allow(clippy::empty_line_after_doc_comments)]
14
+ #![allow(clippy::doc_lazy_continuation)]
15
 
16
  #[macro_use]
17
  pub mod macros;
crates/openwa-core/src/mem.rs CHANGED
@@ -36,7 +36,7 @@ pub unsafe fn classify_pointer(value: u32, delta: u32) -> Option<PointerInfo> {
36
  let ghidra_val = value.wrapping_sub(delta);
37
 
38
  // .rdata → Vtable (likely vtable or function pointer table)
39
- if ghidra_val >= va::RDATA_START && ghidra_val < va::DATA_START {
40
  let detail = if can_read(value, 4) {
41
  let vt0 = *(value as *const u32);
42
  Some(format!("vt[0]=ghidra:0x{:08X}", vt0.wrapping_sub(delta)))
@@ -53,7 +53,7 @@ pub unsafe fn classify_pointer(value: u32, delta: u32) -> Option<PointerInfo> {
53
  }
54
 
55
  // .text → Code
56
- if ghidra_val >= va::TEXT_START && ghidra_val <= va::TEXT_END {
57
  return Some(PointerInfo {
58
  offset: 0,
59
  raw_value: value,
@@ -64,7 +64,7 @@ pub unsafe fn classify_pointer(value: u32, delta: u32) -> Option<PointerInfo> {
64
  }
65
 
66
  // .data/.bss → Data
67
- if ghidra_val >= va::DATA_START && ghidra_val < va::DATA_END {
68
  return Some(PointerInfo {
69
  offset: 0,
70
  raw_value: value,
@@ -80,7 +80,7 @@ pub unsafe fn classify_pointer(value: u32, delta: u32) -> Option<PointerInfo> {
80
  let ghidra_first = first.wrapping_sub(delta);
81
 
82
  // Object — heap pointer whose first DWORD is a vtable
83
- if ghidra_first >= va::RDATA_START && ghidra_first < va::DATA_START {
84
  let detail = if can_read(first, 4) {
85
  let vt0 = *(first as *const u32);
86
  Some(format!(
 
36
  let ghidra_val = value.wrapping_sub(delta);
37
 
38
  // .rdata → Vtable (likely vtable or function pointer table)
39
+ if (va::RDATA_START..va::DATA_START).contains(&ghidra_val) {
40
  let detail = if can_read(value, 4) {
41
  let vt0 = *(value as *const u32);
42
  Some(format!("vt[0]=ghidra:0x{:08X}", vt0.wrapping_sub(delta)))
 
53
  }
54
 
55
  // .text → Code
56
+ if (va::TEXT_START..=va::TEXT_END).contains(&ghidra_val) {
57
  return Some(PointerInfo {
58
  offset: 0,
59
  raw_value: value,
 
64
  }
65
 
66
  // .data/.bss → Data
67
+ if (va::DATA_START..va::DATA_END).contains(&ghidra_val) {
68
  return Some(PointerInfo {
69
  offset: 0,
70
  raw_value: value,
 
80
  let ghidra_first = first.wrapping_sub(delta);
81
 
82
  // Object — heap pointer whose first DWORD is a vtable
83
+ if (va::RDATA_START..va::DATA_START).contains(&ghidra_first) {
84
  let detail = if can_read(first, 4) {
85
  let vt0 = *(first as *const u32);
86
  Some(format!(
crates/openwa-core/src/render/gfx_dir.rs CHANGED
@@ -121,9 +121,9 @@ pub unsafe fn gfx_dir_find_entry(name: *const core::ffi::c_char, gfx_dir: *mut u
121
 
122
  // Find '|' separator in buffer
123
  let mut pipe_pos: Option<usize> = None;
124
- for j in 0..i {
125
- if buf[j] == b'|' {
126
- buf[j] = 0; // truncate at pipe
127
  pipe_pos = Some(j);
128
  break;
129
  }
@@ -139,12 +139,12 @@ pub unsafe fn gfx_dir_find_entry(name: *const core::ffi::c_char, gfx_dir: *mut u
139
  };
140
 
141
  // Lowercase A-Z in buffer
142
- for j in 0..buf.len() {
143
- if buf[j] == 0 {
144
  break;
145
  }
146
- if buf[j] >= b'A' && buf[j] <= b'Z' {
147
- buf[j] += 0x20;
148
  }
149
  }
150
 
 
121
 
122
  // Find '|' separator in buffer
123
  let mut pipe_pos: Option<usize> = None;
124
+ for (j, b) in buf[..i].iter_mut().enumerate() {
125
+ if *b == b'|' {
126
+ *b = 0; // truncate at pipe
127
  pipe_pos = Some(j);
128
  break;
129
  }
 
139
  };
140
 
141
  // Lowercase A-Z in buffer
142
+ for b in buf.iter_mut() {
143
+ if *b == 0 {
144
  break;
145
  }
146
+ if *b >= b'A' && *b <= b'Z' {
147
+ *b += 0x20;
148
  }
149
  }
150
 
crates/openwa-core/src/snapshot.rs CHANGED
@@ -97,7 +97,7 @@ fn is_likely_pointer(val: u32, delta: u32) -> bool {
97
  }
98
  // Check if it's in a known WA section (.text through .data end)
99
  let ghidra = val.wrapping_sub(delta);
100
- if ghidra >= va::TEXT_START && ghidra < va::DATA_END {
101
  return true;
102
  }
103
  // Heap pointer heuristic: > 1MB and readable
@@ -159,7 +159,7 @@ pub unsafe fn hash_pointer_targets(
159
  if !is_likely_pointer(val, delta) { continue; }
160
  // Skip WA code/data pointers — only follow heap pointers
161
  let ghidra = val.wrapping_sub(delta);
162
- if ghidra >= va::TEXT_START && ghidra < va::DATA_END { continue; }
163
  // Verify target is readable
164
  if !mem::can_read(val, target_window as u32) { continue; }
165
 
 
97
  }
98
  // Check if it's in a known WA section (.text through .data end)
99
  let ghidra = val.wrapping_sub(delta);
100
+ if (va::TEXT_START..va::DATA_END).contains(&ghidra) {
101
  return true;
102
  }
103
  // Heap pointer heuristic: > 1MB and readable
 
159
  if !is_likely_pointer(val, delta) { continue; }
160
  // Skip WA code/data pointers — only follow heap pointers
161
  let ghidra = val.wrapping_sub(delta);
162
+ if (va::TEXT_START..va::DATA_END).contains(&ghidra) { continue; }
163
  // Verify target is readable
164
  if !mem::can_read(val, target_window as u32) { continue; }
165
 
crates/openwa-core/src/wa/mfc.rs CHANGED
@@ -73,6 +73,11 @@ impl CStringRef {
73
  *((data - 0x0C) as *const i32)
74
  }
75
 
 
 
 
 
 
76
  /// Assign from another CString via `ATL::CSimpleStringT::operator=` (0x401D20).
77
  /// thiscall(ECX=this, stack=&src) where both are CSimpleStringT* pointers.
78
  pub unsafe fn assign_from(&mut self, src: &CStringRef) {
 
73
  *((data - 0x0C) as *const i32)
74
  }
75
 
76
+ /// Check if the CString is empty.
77
+ pub unsafe fn is_empty(&self) -> bool {
78
+ self.len() == 0
79
+ }
80
+
81
  /// Assign from another CString via `ATL::CSimpleStringT::operator=` (0x401D20).
82
  /// thiscall(ECX=this, stack=&src) where both are CSimpleStringT* pointers.
83
  pub unsafe fn assign_from(&mut self, src: &CStringRef) {
crates/openwa-core/src/wa/resource.rs CHANGED
@@ -1,5 +1,6 @@
1
  //! Win32 PE resource extraction helpers.
2
 
 
3
  type LPCSTR = *const u8;
4
 
5
  extern "system" {
 
1
  //! Win32 PE resource extraction helpers.
2
 
3
+ #[allow(clippy::upper_case_acronyms)]
4
  type LPCSTR = *const u8;
5
 
6
  extern "system" {
crates/openwa-core/src/wa_alloc.rs CHANGED
@@ -105,7 +105,7 @@ impl<T> WABox<T> {
105
  /// in a WA struct field that will eventually be freed by a C++ destructor.
106
  pub fn leak(self) -> *mut T {
107
  let ptr = self.ptr.as_ptr();
108
- core::mem::forget(self);
109
  ptr
110
  }
111
  }
 
105
  /// in a WA struct field that will eventually be freed by a C++ destructor.
106
  pub fn leak(self) -> *mut T {
107
  let ptr = self.ptr.as_ptr();
108
+ let _ = core::mem::ManuallyDrop::new(self);
109
  ptr
110
  }
111
  }
crates/openwa-wormkit/src/debug_watchpoint.rs CHANGED
@@ -132,7 +132,7 @@ unsafe extern "system" fn veh_handler(info: *mut EXCEPTION_POINTERS) -> i32 {
132
  for depth in 0..12 {
133
  // Validate EBP: must be in plausible stack range, aligned,
134
  // and above ESP (stack grows down)
135
- if ebp < 0x10000 || ebp > 0x7FFE0000 || (ebp & 3) != 0 {
136
  break;
137
  }
138
  // Safety: check both [ebp] and [ebp+4] are readable
 
132
  for depth in 0..12 {
133
  // Validate EBP: must be in plausible stack range, aligned,
134
  // and above ESP (stack grows down)
135
+ if !(0x10000..=0x7FFE0000).contains(&ebp) || (ebp & 3) != 0 {
136
  break;
137
  }
138
  // Safety: check both [ebp] and [ebp+4] are readable
crates/openwa-wormkit/src/lib.rs CHANGED
@@ -1,4 +1,7 @@
1
  #![allow(non_snake_case)]
 
 
 
2
 
3
  use std::ffi::c_void;
4
 
 
1
  #![allow(non_snake_case)]
2
+ #![allow(clippy::missing_safety_doc)]
3
+ #![allow(clippy::too_many_arguments)]
4
+ #![allow(clippy::missing_transmute_annotations)]
5
 
6
  use std::ffi::c_void;
7
 
crates/openwa-wormkit/src/replacements/config.rs CHANGED
@@ -244,7 +244,7 @@ unsafe extern "stdcall" fn hook_load_options(game_info: *mut GameInfo) {
244
  // BackgroundDebrisParallax: clamp to i16 range, then << 16
245
  let mut parallax = read_profile_int("Options", "BackgroundDebrisParallax", 0x50);
246
  let parallax_i32 = parallax as i32;
247
- if parallax_i32 < -0x8000 || parallax_i32 > 0x7FFF {
248
  if parallax_i32 < 0 {
249
  parallax = (-0x8000i32) as u32;
250
  } else {
 
244
  // BackgroundDebrisParallax: clamp to i16 range, then << 16
245
  let mut parallax = read_profile_int("Options", "BackgroundDebrisParallax", 0x50);
246
  let parallax_i32 = parallax as i32;
247
+ if !(-0x8000..=0x7FFF).contains(&parallax_i32) {
248
  if parallax_i32 < 0 {
249
  parallax = (-0x8000i32) as u32;
250
  } else {
crates/openwa-wormkit/src/replacements/input.rs CHANGED
@@ -230,7 +230,7 @@ unsafe extern "stdcall" fn hook_turn_manager(turngame: u32) {
230
 
231
  // Milestone detection (check every 50 frames to reduce overhead).
232
  // Skip the first 100 frames to let game state fully initialize.
233
- if frame >= 100 && frame % 50 == 0 {
234
  check_milestones(ddgame, frame);
235
  }
236
  }
 
230
 
231
  // Milestone detection (check every 50 frames to reduce overhead).
232
  // Skip the first 100 frames to let game state fully initialize.
233
+ if frame >= 100 && frame.is_multiple_of(50) {
234
  check_milestones(ddgame, frame);
235
  }
236
  }
crates/openwa-wormkit/src/replacements/render.rs CHANGED
@@ -413,12 +413,11 @@ unsafe extern "stdcall" fn draw_bungee_trail_impl(task_ptr: u32, style: u32, fil
413
  let seg_angle = *(seg_data.add(4 + i as usize * 8) as *const i32);
414
 
415
  // Include vertex if: first segment, or segment has nonzero angle, or fill mode
416
- if i == 0 || seg_angle != 0 || fill != 0 {
417
- if vert_count < MAX_VERTICES {
418
  verts[vert_count] = [x, y, 0];
419
  vert_count += 1;
420
  }
421
- }
422
 
423
  accumulated_angle = accumulated_angle.wrapping_add(seg_angle as u32);
424
 
 
413
  let seg_angle = *(seg_data.add(4 + i as usize * 8) as *const i32);
414
 
415
  // Include vertex if: first segment, or segment has nonzero angle, or fill mode
416
+ if (i == 0 || seg_angle != 0 || fill != 0)
417
+ && vert_count < MAX_VERTICES {
418
  verts[vert_count] = [x, y, 0];
419
  vert_count += 1;
420
  }
 
421
 
422
  accumulated_angle = accumulated_angle.wrapping_add(seg_angle as u32);
423
 
crates/openwa-wormkit/src/replacements/replay.rs CHANGED
@@ -151,7 +151,7 @@ unsafe fn replay_loader_play(gi: *mut GameInfo) -> Result<(), ReplayError> {
151
  let _ = std::fs::write(&thm_path, payload_slice);
152
  } else {
153
  (*gi).replay_payload_2 = *(payload.add(4) as *const i32);
154
- if first_dword >= -4 && first_dword < -2 {
155
  *((*gi).replay_payload_extra.as_mut_ptr() as *mut i32) =
156
  *(payload.add(8) as *const i32);
157
  } else if first_dword == -2 {
@@ -202,7 +202,7 @@ unsafe fn replay_loader_play(gi: *mut GameInfo) -> Result<(), ReplayError> {
202
  let wa_log_file = *(rb(va::G_LOG_FILE_PTR) as *const *mut FILE);
203
  if !wa_log_file.is_null() {
204
  if let Some(mut log_file) = wa_file_to_rust(wa_log_file) {
205
- write_replay_log(gi, &mut *log_file)?;
206
  }
207
  }
208
  let _ = log_line("[Replay] Rust replay loading complete");
@@ -258,7 +258,7 @@ unsafe fn parse_and_write_v2plus(
258
  wb(va::G_REPLAY_GAME_MODE, mode);
259
  } else {
260
  let raw = s.read_u8_validated(0, 3)?;
261
- if raw >= 2 && raw <= 3 {
262
  wb(va::G_REPLAY_GAME_MODE, raw - 1);
263
  wb(va::G_REPLAY_VER_FLAG_A, 1); wb(va::G_REPLAY_VER_FLAG_B, 1);
264
  } else {
@@ -304,7 +304,7 @@ unsafe fn parse_and_write_v2plus(
304
  wd(va::G_REPLAY_SCHEME_PRESENT, scheme_present as u32);
305
 
306
  // Extra field for version 7-9 only
307
- if version >= 7 && version <= 9 {
308
  let _extra = s.read_u32()?;
309
  }
310
 
@@ -337,7 +337,7 @@ unsafe fn parse_and_write_v2plus(
337
  1 => 0xD8_usize,
338
  2 => 0x124,
339
  3 => {
340
- if scheme_size_indicator < 0x12A || scheme_size_indicator > 0x197 {
341
  return Err(ReplayError::InvalidFormat);
342
  }
343
  // Copy defaults first for v3
@@ -558,7 +558,7 @@ unsafe fn parse_and_write_v2plus(
558
  wd(va::G_SAVED_RANDOM_SEED, current_seed); // save old seed
559
 
560
  let ver = *(rb(va::G_REPLAY_VERSION_ID) as *const i32);
561
- if ver != 0x22 && !(ver >= 0x29 && ver <= 0x2A) && ver < 0x2D {
562
  let check: unsafe extern "cdecl" fn() -> i32 =
563
  core::mem::transmute(rb(va::SCHEME_CHECK_WEAPON_LIMITS));
564
  check();
@@ -635,8 +635,8 @@ unsafe fn write_replay_log(gi: *const GameInfo, log_file: &mut File) -> Result<(
635
  if !tm.is_null() {
636
  let tm = &*tm;
637
  let mut s = heapless::String::<128>::new();
638
- let _ = write!(s,
639
- "Game Started at {:04}-{:02}-{:02} {:02}:{:02}:{:02} GMT\n",
640
  tm[5] + 1900, tm[4] + 1, tm[3], tm[2], tm[1], tm[0]
641
  );
642
  let _ = log_file.write_all(s.as_bytes());
@@ -673,11 +673,11 @@ unsafe fn write_replay_log(gi: *const GameInfo, log_file: &mut File) -> Result<(
673
  push_cstr(&mut s, label_game_engine);
674
  let _ = write!(s, ": ");
675
  push_cstr(&mut s, version_str);
676
- let _ = write!(s, "\n");
677
  push_cstr(&mut s, label_file_format);
678
  let _ = write!(s, ": ");
679
  push_cstr(&mut s, format_ver_str);
680
- let _ = write!(s, "\n");
681
  let _ = log_file.write_all(s.as_bytes());
682
  }
683
 
@@ -776,7 +776,7 @@ unsafe fn write_replay_log(gi: *const GameInfo, log_file: &mut File) -> Result<(
776
  let _ = write!(s, " {whole}.{frac:02}]");
777
  }
778
 
779
- let _ = write!(s, "\n");
780
  let _ = log_file.write_all(s.as_bytes());
781
  }
782
 
 
151
  let _ = std::fs::write(&thm_path, payload_slice);
152
  } else {
153
  (*gi).replay_payload_2 = *(payload.add(4) as *const i32);
154
+ if (-4..-2).contains(&first_dword) {
155
  *((*gi).replay_payload_extra.as_mut_ptr() as *mut i32) =
156
  *(payload.add(8) as *const i32);
157
  } else if first_dword == -2 {
 
202
  let wa_log_file = *(rb(va::G_LOG_FILE_PTR) as *const *mut FILE);
203
  if !wa_log_file.is_null() {
204
  if let Some(mut log_file) = wa_file_to_rust(wa_log_file) {
205
+ write_replay_log(gi, &mut log_file)?;
206
  }
207
  }
208
  let _ = log_line("[Replay] Rust replay loading complete");
 
258
  wb(va::G_REPLAY_GAME_MODE, mode);
259
  } else {
260
  let raw = s.read_u8_validated(0, 3)?;
261
+ if (2..=3).contains(&raw) {
262
  wb(va::G_REPLAY_GAME_MODE, raw - 1);
263
  wb(va::G_REPLAY_VER_FLAG_A, 1); wb(va::G_REPLAY_VER_FLAG_B, 1);
264
  } else {
 
304
  wd(va::G_REPLAY_SCHEME_PRESENT, scheme_present as u32);
305
 
306
  // Extra field for version 7-9 only
307
+ if (7..=9).contains(&version) {
308
  let _extra = s.read_u32()?;
309
  }
310
 
 
337
  1 => 0xD8_usize,
338
  2 => 0x124,
339
  3 => {
340
+ if !(0x12A..=0x197).contains(&scheme_size_indicator) {
341
  return Err(ReplayError::InvalidFormat);
342
  }
343
  // Copy defaults first for v3
 
558
  wd(va::G_SAVED_RANDOM_SEED, current_seed); // save old seed
559
 
560
  let ver = *(rb(va::G_REPLAY_VERSION_ID) as *const i32);
561
+ if ver != 0x22 && !(0x29..=0x2A).contains(&ver) && ver < 0x2D {
562
  let check: unsafe extern "cdecl" fn() -> i32 =
563
  core::mem::transmute(rb(va::SCHEME_CHECK_WEAPON_LIMITS));
564
  check();
 
635
  if !tm.is_null() {
636
  let tm = &*tm;
637
  let mut s = heapless::String::<128>::new();
638
+ let _ = writeln!(s,
639
+ "Game Started at {:04}-{:02}-{:02} {:02}:{:02}:{:02} GMT",
640
  tm[5] + 1900, tm[4] + 1, tm[3], tm[2], tm[1], tm[0]
641
  );
642
  let _ = log_file.write_all(s.as_bytes());
 
673
  push_cstr(&mut s, label_game_engine);
674
  let _ = write!(s, ": ");
675
  push_cstr(&mut s, version_str);
676
+ let _ = writeln!(s);
677
  push_cstr(&mut s, label_file_format);
678
  let _ = write!(s, ": ");
679
  push_cstr(&mut s, format_ver_str);
680
+ let _ = writeln!(s);
681
  let _ = log_file.write_all(s.as_bytes());
682
  }
683
 
 
776
  let _ = write!(s, " {whole}.{frac:02}]");
777
  }
778
 
779
+ let _ = writeln!(s);
780
  let _ = log_file.write_all(s.as_bytes());
781
  }
782
 
crates/openwa-wormkit/src/replacements/scheme.rs CHANGED
@@ -441,7 +441,7 @@ fn parse_scheme_slot(filename: &str) -> Option<u32> {
441
  }
442
  let slot = d0 as u32 * 10 + d1 as u32;
443
  // Valid range: 1-13 (original checks `slot - 1 < 0xD`)
444
- if slot >= 1 && slot <= SCHEME_SLOT_COUNT {
445
  Some(slot)
446
  } else {
447
  None
 
441
  }
442
  let slot = d0 as u32 * 10 + d1 as u32;
443
  // Valid range: 1-13 (original checks `slot - 1 < 0xD`)
444
+ if (1..=SCHEME_SLOT_COUNT).contains(&slot) {
445
  Some(slot)
446
  } else {
447
  None
crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -92,11 +92,10 @@ unsafe extern "cdecl" fn get_ammo_impl(
92
  }
93
 
94
  // SelectWorm (0x3B) requires >1 alive worm on the team
95
- if state.game_phase >= ddgame::GAME_PHASE_NORMAL_MIN && weapon_id == Weapon::SelectWorm as u32 {
96
- if count_alive_worms_impl(team_index, arena) == 0 {
97
  return 0;
98
  }
99
- }
100
 
101
  state.get_ammo(alliance, wid) as u32
102
  }
 
92
  }
93
 
94
  // SelectWorm (0x3B) requires >1 alive worm on the team
95
+ if state.game_phase >= ddgame::GAME_PHASE_NORMAL_MIN && weapon_id == Weapon::SelectWorm as u32
96
+ && count_alive_worms_impl(team_index, arena) == 0 {
97
  return 0;
98
  }
 
99
 
100
  state.get_ammo(alliance, wid) as u32
101
  }
crates/openwa-wormkit/src/validation/mod.rs CHANGED
@@ -637,7 +637,7 @@ fn dump_team_blocks() {
637
 
638
  // Dump first 16 bytes at each team_index * 0x51C stride (slots -2 to 7)
639
  for slot in -2i32..8 {
640
- let offset = slot as isize * 0x51C as isize;
641
  let ptr = tws_base.offset(offset);
642
  let mut hex = String::new();
643
  for i in 0..16usize {
 
637
 
638
  // Dump first 16 bytes at each team_index * 0x51C stride (slots -2 to 7)
639
  for slot in -2i32..8 {
640
+ let offset = slot as isize * 0x51C_isize;
641
  let ptr = tws_base.offset(offset);
642
  let mut hex = String::new();
643
  for i in 0..16usize {