use crate::simple_pack::SimplePack; use std::collections::HashSet; use std::path::Path; /// Generate crypt.json.zst from crypt.json with minimum format pub fn gen_crypt + ?Sized, D: AsRef + ?Sized>( json_path: &P, outdir: &D, level: i32, ) -> std::io::Result<()> { let p = json_path.as_ref(); let json_data = std::fs::read_to_string(p)?; let json = json::parse(&json_data) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; let out_data = json::stringify(json); let out_path = outdir.as_ref().join("crypt.json.zst"); let mut out_file = std::io::BufWriter::new(std::fs::File::create(out_path)?); let level = if level >= 0 && level <= 22 { level } else { 22 }; let mut encoder = zstd::stream::write::Encoder::new(&mut out_file, level)?; std::io::copy(&mut out_data.as_bytes(), &mut encoder)?; encoder.finish()?; Ok(()) } /// Pack all binary files in cx_cb into a single archive. pub fn gen_cx_cb + ?Sized, D: AsRef + ?Sized>( json_path: &P, outdir: &D, level: i32, ) -> std::io::Result<()> { let p = json_path.as_ref(); let pb = p.parent().unwrap_or_else(|| Path::new("")).join("cx_cb"); let json_data = std::fs::read_to_string(p)?; let json = json::parse(&json_data) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; let mut pack = SimplePack::new(&outdir.as_ref().join("cx_cb.pck"))?; let mut seen_files = HashSet::new(); for (_, obj) in json.entries() { if let Some(name) = obj["ControlBlockName"].as_str() { if seen_files.contains(name) { continue; } seen_files.insert(name.to_string()); let file_path = pb.join(name); if !file_path.exists() { return Err(std::io::Error::new( std::io::ErrorKind::NotFound, format!("File not found: {}", file_path.display()), )); } let file = std::fs::File::open(file_path)?; let file_size = file.metadata()?.len(); let typ = obj["$type"].as_str().unwrap(); if typ == "HxCryptLite" { if file_size != 0x3000 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("File size must be 12288 bytes: {}", name), )); } } else { if file_size != 4096 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("File size must be 4096 bytes: {}", name), )); } } let file = std::io::BufReader::new(file); pack.add_file(name, file)?; } } if level >= 0 && level <= 22 { pack.compress(level)?; } Ok(()) } /// Pack all binary files in name_list into a single archive. pub fn gen_name_list + ?Sized, D: AsRef + ?Sized>( json_path: &P, outdir: &D, level: i32, ) -> std::io::Result<()> { let p = json_path.as_ref(); let pb = p .parent() .unwrap_or_else(|| Path::new("")) .join("name_list"); let json_data = std::fs::read_to_string(p)?; let json = json::parse(&json_data) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; let mut pack = SimplePack::new(&outdir.as_ref().join("name_list.pck"))?; let mut seen_files = HashSet::new(); for (_, obj) in json.entries() { if let Some(name) = obj["FileListName"].as_str() { if seen_files.contains(name) { continue; } seen_files.insert(name.to_string()); let file_path = pb.join(name); if !file_path.exists() { return Err(std::io::Error::new( std::io::ErrorKind::NotFound, format!("File not found: {}", file_path.display()), )); } let file = std::fs::File::open(file_path)?; let file = std::io::BufReader::new(file); pack.add_file(name, file)?; } } if level >= 0 && level <= 22 { pack.compress(level)?; } Ok(()) }