feat: add Debug formatter for GameInfo known fields
Browse filesPrints all named options fields, data paths as strings, and unknown
config DWORDs in hex. Skips the large unknown padding regions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
crates/openwa-types/src/game_info.rs
CHANGED
|
@@ -81,3 +81,59 @@ pub struct GameInfo {
|
|
| 81 |
}
|
| 82 |
|
| 83 |
const _: () = assert!(core::mem::size_of::<GameInfo>() == 0xF500);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
}
|
| 82 |
|
| 83 |
const _: () = assert!(core::mem::size_of::<GameInfo>() == 0xF500);
|
| 84 |
+
|
| 85 |
+
struct HexU32s<'a>(&'a [u32]);
|
| 86 |
+
|
| 87 |
+
impl core::fmt::Debug for HexU32s<'_> {
|
| 88 |
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
| 89 |
+
write!(f, "[")?;
|
| 90 |
+
for (i, v) in self.0.iter().enumerate() {
|
| 91 |
+
if i > 0 { write!(f, ", ")?; }
|
| 92 |
+
write!(f, "0x{v:08X}")?;
|
| 93 |
+
}
|
| 94 |
+
write!(f, "]")
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
impl core::fmt::Debug for GameInfo {
|
| 99 |
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
| 100 |
+
// Extract land_dat_path as a string (null-terminated)
|
| 101 |
+
let land_str = self.land_dat_path.iter()
|
| 102 |
+
.position(|&b| b == 0)
|
| 103 |
+
.map(|end| core::str::from_utf8(&self.land_dat_path[..end]).unwrap_or("<invalid utf8>"))
|
| 104 |
+
.unwrap_or(core::str::from_utf8(&self.land_dat_path).unwrap_or("<invalid utf8>"));
|
| 105 |
+
|
| 106 |
+
// Extract speech_path as a string (null-terminated)
|
| 107 |
+
let speech_str = self.speech_path.iter()
|
| 108 |
+
.position(|&b| b == 0)
|
| 109 |
+
.map(|end| core::str::from_utf8(&self.speech_path[..end]).unwrap_or("<invalid utf8>"))
|
| 110 |
+
.unwrap_or(core::str::from_utf8(&self.speech_path).unwrap_or("<invalid utf8>"));
|
| 111 |
+
|
| 112 |
+
f.debug_struct("GameInfo")
|
| 113 |
+
// Cluster 1: data paths
|
| 114 |
+
.field("_config_dword_dae8", &format_args!("0x{:08X}", self._config_dword_dae8))
|
| 115 |
+
.field("land_dat_path", &land_str)
|
| 116 |
+
// Cluster 2: game options
|
| 117 |
+
.field("_config_byte_f3a0", &self._config_byte_f3a0)
|
| 118 |
+
.field("detail_level", &self.detail_level)
|
| 119 |
+
.field("energy_bar", &self.energy_bar)
|
| 120 |
+
.field("info_transparency", &self.info_transparency)
|
| 121 |
+
.field("info_spy", &self.info_spy)
|
| 122 |
+
.field("chat_pinned", &self.chat_pinned)
|
| 123 |
+
.field("chat_lines", &self.chat_lines)
|
| 124 |
+
.field("pinned_chat_lines", &format_args!("0x{:08X}", self.pinned_chat_lines))
|
| 125 |
+
.field("home_lock", &self.home_lock)
|
| 126 |
+
.field("_config_block_f3b4", &HexU32s(&self._config_block_f3b4))
|
| 127 |
+
.field("_config_dword_f3d4", &format_args!("0x{:08X}", self._config_dword_f3d4))
|
| 128 |
+
.field("_config_dword_f3d8", &format_args!("0x{:08X}", self._config_dword_f3d8))
|
| 129 |
+
.field("capture_transparent_pngs", &self.capture_transparent_pngs)
|
| 130 |
+
.field("camera_unlock_mouse_speed", &self.camera_unlock_mouse_speed)
|
| 131 |
+
.field("_config_dword_f3e4", &format_args!("0x{:08X}", self._config_dword_f3e4))
|
| 132 |
+
.field("background_debris_parallax", &format_args!("0x{:08X}", self.background_debris_parallax))
|
| 133 |
+
.field("topmost_explosion_onomatopoeia", &self.topmost_explosion_onomatopoeia)
|
| 134 |
+
.field("_zeroed_f3f0", &self._zeroed_f3f0)
|
| 135 |
+
.field("_conditional_config_f3f4", &HexU32s(&self._conditional_config_f3f4))
|
| 136 |
+
.field("speech_path", &speech_str)
|
| 137 |
+
.finish()
|
| 138 |
+
}
|
| 139 |
+
}
|