rustlol 0.1.1

A wad files lib
Documentation
use std::fs::File;
use std::io::{Error, Write};
use std::path::Path;

use log::info;

use crate::lol::io::sys::LOLfile;
use crate::lol::io::sys_type::Lolfileresult;
use crate::lol::wad::archive::Archive;

pub mod lol;
pub mod zstd_compatibility_test;
pub mod wad_diagnostic;

    /// extract api args (src: [str], dict_path:[str] if dict_patch is "" it defult = "./hashes.game.txt" dst: [str] 
    /// if dst is src - ".client" ) and make a name is src dir
    /// # Arguments
    /// - `src`: The first arg [&str]
    /// - `dict_path` : the Second arg [&str]  if dict_patch is "" it defult = "./hashes.game.txt"
    /// - `dsit` : the Third arg [&str] if dst is src - ".client" 
    ///  
    /// # Example
    /// ```rust
    ///     fn test_extract_wad() {
    ///         extract_wad("./youfile.wad.client", "", "").unwrap();
    ///     }
    /// ```
    /// extract PATH TO "./youfile.wad.client" Gen a youfile.wad dir 
    /// 
    pub fn extract_wad(src: &str, dict_path: &str, dst: &str) -> std::result::Result<(), Error> {
        let trace = lol_trace_func!(extract_wad, lol_trace_var!("{}", src), lol_trace_var!("{}", dict_path));
        lol_throw_if!(src.is_empty(), trace);
        let mut dict =  lol::hash::Dict::new();
        if dict_path.is_empty() {
            let res = dict.load("./hashes.game.txt");
            if res.is_err() {
                return Err(res.err().unwrap());
            }            
        } else {
            let res = dict.load(dict_path);
            if res.is_err() {
                return Err(res.err().unwrap());
            }  
        }

        let path = Path::new(src);
        let mut newpath = String::new();
         if path.extension().and_then(|s| s.to_str()) ==Some("client") {
            if dst.is_empty() {
                newpath =  path.with_extension("").to_str().unwrap().to_string();
                if Path::new(&newpath).is_dir() {
                        let _=   path.to_path_buf();
                } else {
                    std::fs::create_dir(&newpath).unwrap();
                }
            } else {
                let pp = path.with_extension("");
                let pa = pp.to_str().unwrap();
                let pa = Path::new(pa);
                newpath = Path::new(dst).to_string_lossy().to_string() + "/" + pa.file_name().unwrap().to_str().unwrap();
                if Path::new(dst).is_dir() {
                    
                } else {
                     std::fs::create_dir_all(dst).unwrap();
                }
            }
            
         }
        

        let a = Archive::read_from_file(Path::new(src));
        for mut entry in a.entries {
            let name = entry.0;
            let (data, size, tpye_) = entry.1.decompressed_bytes();
            //println!("name {:16x}", name);
            let fake_name =format!("{:16x}.bin", name, );
            let filename  = dict.get(name).unwrap_or(&fake_name );
            let mut _file_name = String::new();
            
            

            if filename.len() > 127 {
                _file_name =  format!("{:016x}", name) + "." +
                Path::new(filename)
                    .extension().unwrap().to_str().unwrap();
            } else {
                _file_name = filename.clone();
            }
            let path = Path::new(&newpath);
            let _file_name = path.join(_file_name);

            let dir = _file_name.parent().unwrap();
            info!("name {:16x} {:?} {:?}", name, _file_name, tpye_);
            std::fs::create_dir_all(dir).unwrap();
            let mut file = File::create(_file_name).unwrap();
            
            file.write_all(&data).unwrap();
            file.set_len(size as _).unwrap();

        };

        LOLfile::file_munmap(0).unwrap();

        Ok(())
        
    }    




    /// make api args (src: [str], dst: [str] 
    /// if dst is src - ".client" ) and make a name is src+".client" file
    /// # Arguments
    /// - `src`: The first arg [&str]
    /// - `dsit` : the Third arg [&str] if `dst` is "" `src` + ".client" 
    ///  
    /// # Example
    /// ```rust
    ///
    ///     fn test_make_wad() {
    ///         make_wad("./youfile.wad", "").unwrap();
    ///     }
    /// 
    /// 
    /// ```
    /// 
    pub fn make_wad(src: &str, dst: &str) -> std::result::Result<(), Error> {
        let src = Path::new(src);
        let mut archive = lol::wad::archive::Archive::new();

        for entry in walkdir::WalkDir::new(src) {
            let entry = entry.expect("walkdir error");
            if !entry.file_type().is_file() {
                continue;
            }
            let path = entry.path();
            info!("Reading: {}",  path.display());
            archive.add_from_directory(path, src).unwrap();
        }    
        
        //let a = archive.entries.get_key_value(&u64::from_str_radix("1a0cb6f38a9f1355", 16).unwrap()).unwrap();
        let mut dst = dst;
        if dst.is_empty() {
            let tmp = format!("{}.client", src.to_str().unwrap());
            dst = &tmp;
            archive.write_file(Path::new(dst))?;
        } else {
            archive.write_file(Path::new(dst))?;
        }

       
        //assert_eq!(true, res.is_ok());
        Ok(())
    }



#[cfg(test)]
mod tests {
    use std::path::Path;

    use crate::{extract_wad, make_wad};

    
    #[test]
    fn test_extract_wad() {
        
       let res =  extract_wad("./Irelia.wad.client", "", "");
       assert_eq!(true, res.is_ok());

    //    let hash = lol::hash::Xxh64::from_path(Path::new("./f1f963a444962d94.bin"));
    //    let s ="./assets/characters/irelia/skins/skin45/particles/irelia_skin45_r_wall01.tex";
    //    let ha  = lol::hash::Xxh64::from_path(Path::new(s));
    //     println!("hash {:16x}", hash.0);
    //      println!("ha {:16x}", ha.0);

     // make_wad();
        
    }

    #[test]
    fn test_make_wad() {
        let res = make_wad("./Irelia.wad", "");
        assert_eq!(true, res.is_ok())
    }

    #[test]
    fn test_zstd_compatibility() {
        crate::zstd_compatibility_test::test_zstd_compatibility();
    }

    #[test]
    fn test_wad_diagnostic() {
        use crate::wad_diagnostic::WADDiagnostic;
        
        // 诊断Rust生成的WAD文件
        let rust_wad_path = Path::new("./Irelia.wad.client");
        if rust_wad_path.exists() {
            println!("诊断Rust生成的WAD文件:");
            WADDiagnostic::diagnose_wad_file(rust_wad_path);
        } else {
            println!("Rust WAD文件不存在,请先运行 test_dict 生成文件");
        }
        
        // 如果有Python版本的WAD文件,进行对比
        let python_wad_path = Path::new("./python_generated.wad.client");
        if rust_wad_path.exists() && python_wad_path.exists() {
            println!("\n对比Rust和Python生成的WAD文件:");
            WADDiagnostic::compare_wad_files(rust_wad_path, python_wad_path);
        }
    }    
}