rustlol 0.1.1

A wad files lib
Documentation
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

/// WAD文件诊断工具
pub struct WADDiagnostic;

impl WADDiagnostic {
    /// 诊断WAD文件的结构和内容
    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;
            }
        }
    }
    
    /// 检查WAD头部
    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);
            }
        }
    }
    
    /// 检查TOC(目录表)
    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;
        }
        
        // 检查前几个TOC条目
        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]; // Raw, Link, Gzip, Zstd, ZstdMulti
        
        // 检查所有条目的数据范围
        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);
            }
        }
    }
    
    /// 对比两个WAD文件的差异
    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 测试");
        }
    }
}