use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
pub struct WADDiagnostic;
impl WADDiagnostic {
pub fn diagnose_wad_file(path: &Path) {
println!("=== WAD文件诊断 ===");
println!("文件路径: {:?}", path);
match File::open(path) {
Ok(mut file) => {
Self::check_file_structure(&mut file);
Self::check_header(&mut file);
Self::check_toc(&mut file);
Self::check_data_integrity(&mut file);
},
Err(e) => {
println!("❌ 无法打开文件: {}", e);
return;
}
}
}
fn check_file_structure(file: &mut File) {
println!("\n--- 文件结构检查 ---");
match file.metadata() {
Ok(metadata) => {
let file_size = metadata.len();
println!("✓ 文件大小: {} bytes ({:.2} MB)", file_size, file_size as f64 / 1024.0 / 1024.0);
if file_size < 272 {
println!("❌ 文件太小,无法包含有效的WAD头部");
return;
}
},
Err(e) => {
println!("❌ 无法获取文件元数据: {}", e);
return;
}
}
}
fn check_header(file: &mut File) {
println!("\n--- 头部检查 ---");
file.seek(SeekFrom::Start(0)).unwrap();
let mut header_buf = vec![0u8; 272];
match file.read_exact(&mut header_buf) {
Ok(()) => {
let signature = &header_buf[0..2];
if signature == b"RW" {
println!("✓ 签名正确: RW");
} else {
println!("❌ 签名错误: {:?} (应该是 RW)",
String::from_utf8_lossy(signature));
return;
}
let major = header_buf[2];
let minor = header_buf[3];
println!("✓ 版本: {}.{}", major, minor);
if major != 3 || minor != 4 {
println!("⚠️ 版本可能不兼容,游戏期望 3.4");
}
let file_signature = &header_buf[4..20];
let signature_hex = file_signature.iter()
.map(|b| format!("{:02X}", b))
.collect::<Vec<_>>()
.join(" ");
println!("文件签名: {}", signature_hex);
let checksum = &header_buf[260..268];
let checksum_hex = checksum.iter()
.map(|b| format!("{:02X}", b))
.collect::<Vec<_>>()
.join(" ");
println!("校验和: {}", checksum_hex);
let entry_count = u32::from_le_bytes([
header_buf[268], header_buf[269], header_buf[270], header_buf[271]
]);
println!("✓ 条目数量: {}", entry_count);
if entry_count == 0 {
println!("❌ 条目数量为0,这是无效的WAD文件");
} else if entry_count > 100000 {
println!("⚠️ 条目数量异常大: {}", entry_count);
}
},
Err(e) => {
println!("❌ 无法读取头部: {}", e);
}
}
}
fn check_toc(file: &mut File) {
println!("\n--- TOC检查 ---");
file.seek(SeekFrom::Start(268)).unwrap();
let mut count_buf = [0u8; 4];
file.read_exact(&mut count_buf).unwrap();
let entry_count = u32::from_le_bytes(count_buf);
if entry_count == 0 {
return;
}
file.seek(SeekFrom::Start(272)).unwrap();
let entries_to_check = std::cmp::min(entry_count, 5) as usize;
for i in 0..entries_to_check {
let mut entry_buf = [0u8; 32];
match file.read_exact(&mut entry_buf) {
Ok(()) => {
let hash = u64::from_le_bytes([
entry_buf[0], entry_buf[1], entry_buf[2], entry_buf[3],
entry_buf[4], entry_buf[5], entry_buf[6], entry_buf[7]
]);
let offset = u32::from_le_bytes([
entry_buf[8], entry_buf[9], entry_buf[10], entry_buf[11]
]);
let compressed_size = u32::from_le_bytes([
entry_buf[12], entry_buf[13], entry_buf[14], entry_buf[15]
]);
let decompressed_size = u32::from_le_bytes([
entry_buf[16], entry_buf[17], entry_buf[18], entry_buf[19]
]);
let compression_type = entry_buf[20];
let is_duplicate = entry_buf[21] != 0;
let checksum = u64::from_le_bytes([
entry_buf[24], entry_buf[25], entry_buf[26], entry_buf[27],
entry_buf[28], entry_buf[29], entry_buf[30], entry_buf[31]
]);
println!("条目 {}: 哈希={:016X}, 偏移={}, 压缩大小={}, 原始大小={}, 类型={}, 重复={}, 校验和={:016X}",
i, hash, offset, compressed_size, decompressed_size,
compression_type, is_duplicate, checksum);
if compressed_size == 0 && decompressed_size > 0 {
println!(" ⚠️ 压缩大小为0但原始大小不为0");
}
if compression_type > 4 {
println!(" ⚠️ 未知的压缩类型: {}", compression_type);
}
if offset < 272 + (entry_count * 32) as u32 {
println!(" ❌ 数据偏移量无效,与头部/TOC重叠");
}
},
Err(e) => {
println!("❌ 无法读取条目 {}: {}", i, e);
break;
}
}
}
}
fn check_data_integrity(file: &mut File) {
println!("\n--- 数据完整性检查 ---");
file.seek(SeekFrom::Start(268)).unwrap();
let mut count_buf = [0u8; 4];
file.read_exact(&mut count_buf).unwrap();
let entry_count = u32::from_le_bytes(count_buf);
if entry_count == 0 {
return;
}
let file_size = file.metadata().unwrap().len();
let mut max_data_end = 0u64;
let mut total_compressed_size = 0u64;
let mut compression_stats = [0u32; 5];
file.seek(SeekFrom::Start(272)).unwrap();
for i in 0..entry_count {
let mut entry_buf = [0u8; 32];
if file.read_exact(&mut entry_buf).is_err() {
println!("❌ 无法读取条目 {}", i);
break;
}
let offset = u32::from_le_bytes([
entry_buf[8], entry_buf[9], entry_buf[10], entry_buf[11]
]) as u64;
let compressed_size = u32::from_le_bytes([
entry_buf[12], entry_buf[13], entry_buf[14], entry_buf[15]
]) as u64;
let compression_type = entry_buf[20];
let is_duplicate = entry_buf[21] != 0;
if !is_duplicate {
let data_end = offset + compressed_size;
max_data_end = max_data_end.max(data_end);
total_compressed_size += compressed_size;
}
if compression_type <= 4 {
compression_stats[compression_type as usize] += 1;
}
if offset + compressed_size > file_size {
println!("❌ 条目 {} 的数据超出文件范围: 偏移={}, 大小={}, 文件大小={}",
i, offset, compressed_size, file_size);
}
}
println!("✓ 最大数据结束位置: {} bytes", max_data_end);
println!("✓ 总压缩数据大小: {} bytes", total_compressed_size);
if max_data_end > file_size {
println!("❌ 数据超出文件大小");
} else {
println!("✓ 数据在文件范围内");
}
println!("\n压缩类型统计:");
let type_names = ["Raw", "Link", "Gzip", "Zstd", "ZstdMulti"];
for (i, &count) in compression_stats.iter().enumerate() {
if count > 0 {
println!(" {}: {} 个条目", type_names[i], count);
}
}
}
pub fn compare_wad_files(rust_path: &Path, python_path: &Path) {
println!("\n=== WAD文件对比 ===");
println!("Rust版本: {:?}", rust_path);
println!("Python版本: {:?}", python_path);
let rust_file = File::open(rust_path);
let python_file = File::open(python_path);
match (rust_file, python_file) {
(Ok(mut rf), Ok(mut pf)) => {
Self::compare_headers(&mut rf, &mut pf);
Self::compare_toc_structure(&mut rf, &mut pf);
},
_ => {
println!("❌ 无法打开一个或两个文件进行对比");
}
}
}
fn compare_headers(rust_file: &mut File, python_file: &mut File) {
println!("\n--- 头部对比 ---");
let mut rust_header = vec![0u8; 272];
let mut python_header = vec![0u8; 272];
rust_file.seek(SeekFrom::Start(0)).unwrap();
python_file.seek(SeekFrom::Start(0)).unwrap();
if rust_file.read_exact(&mut rust_header).is_ok() &&
python_file.read_exact(&mut python_header).is_ok() {
if rust_header == python_header {
println!("✓ 头部完全相同");
} else {
println!("❌ 头部存在差异");
Self::compare_field("签名", &rust_header[0..2], &python_header[0..2]);
Self::compare_field("版本", &rust_header[2..4], &python_header[2..4]);
Self::compare_field("文件签名", &rust_header[4..20], &python_header[4..20]);
Self::compare_field("校验和", &rust_header[260..268], &python_header[260..268]);
Self::compare_field("条目数量", &rust_header[268..272], &python_header[268..272]);
}
}
}
fn compare_toc_structure(rust_file: &mut File, python_file: &mut File) {
println!("\n--- TOC结构对比 ---");
rust_file.seek(SeekFrom::Start(268)).unwrap();
python_file.seek(SeekFrom::Start(268)).unwrap();
let mut count_buf = [0u8; 4];
rust_file.read_exact(&mut count_buf).unwrap();
let rust_count = u32::from_le_bytes(count_buf);
python_file.read_exact(&mut count_buf).unwrap();
let python_count = u32::from_le_bytes(count_buf);
println!("Rust条目数: {}, Python条目数: {}", rust_count, python_count);
if rust_count != python_count {
println!("❌ 条目数量不同");
return;
}
rust_file.seek(SeekFrom::Start(272)).unwrap();
python_file.seek(SeekFrom::Start(272)).unwrap();
let entries_to_compare = std::cmp::min(rust_count, 3) as usize;
for i in 0..entries_to_compare {
let mut rust_entry = [0u8; 32];
let mut python_entry = [0u8; 32];
if rust_file.read_exact(&mut rust_entry).is_ok() &&
python_file.read_exact(&mut python_entry).is_ok() {
if rust_entry == python_entry {
println!("✓ 条目 {} 相同", i);
} else {
println!("❌ 条目 {} 不同", i);
Self::compare_field("哈希", &rust_entry[0..8], &python_entry[0..8]);
Self::compare_field("偏移", &rust_entry[8..12], &python_entry[8..12]);
Self::compare_field("压缩大小", &rust_entry[12..16], &python_entry[12..16]);
Self::compare_field("原始大小", &rust_entry[16..20], &python_entry[16..20]);
Self::compare_field("压缩类型", &rust_entry[20..21], &python_entry[20..21]);
Self::compare_field("重复标志", &rust_entry[21..22], &python_entry[21..22]);
Self::compare_field("校验和", &rust_entry[24..32], &python_entry[24..32]);
}
}
}
}
fn compare_field(name: &str, rust_data: &[u8], python_data: &[u8]) {
if rust_data == python_data {
println!(" ✓ {}: 相同", name);
} else {
let rust_hex = rust_data.iter().map(|b| format!("{:02X}", b)).collect::<Vec<_>>().join(" ");
let python_hex = python_data.iter().map(|b| format!("{:02X}", b)).collect::<Vec<_>>().join(" ");
println!(" ❌ {}: Rust=[{}], Python=[{}]", name, rust_hex, python_hex);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diagnose_rust_wad() {
let path = Path::new("./Irelia.wad.client");
if path.exists() {
WADDiagnostic::diagnose_wad_file(path);
} else {
println!("WAD文件不存在,请先运行 make_wad 测试");
}
}
}