use std::{fs::File, io::Write}; use clap::Parser; use destiny_pkg::{ package::{classify_file_prebl, PackagePlatform}, GameVersion, PackageManager, TagHash, }; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None, disable_version_flag(true))] struct Args { /// Path to packages directory packages_path: String, /// List of tags to extract tags: Vec, /// Directory to extract to #[arg(short, default_value = "./out/")] output_dir: String, /// Version of the package to extract #[arg(short, value_enum)] version: GameVersion, #[arg(short, value_enum)] platform: Option, } fn main() -> anyhow::Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); let args = Args::parse(); let package_manager = PackageManager::new(args.packages_path, args.version, args.platform)?; for t in &args.tags { let tag = TagHash(u32::from_be(u32::from_str_radix(t, 16)?)); let Some(entry) = package_manager.get_entry(tag) else { eprintln!("Tag {} does not exist!", tag); continue; }; let pkg_path = package_manager.package_paths.get(&tag.pkg_id()).unwrap(); let pkg_name = &pkg_path.filename; let out_dir = args.output_dir.clone(); let ext = if args.version == GameVersion::Destiny2Shadowkeep { classify_file_prebl(entry.file_type, entry.file_subtype) } else { "bin".to_string() }; std::fs::create_dir_all(&out_dir).ok(); let ref_hash = TagHash(entry.reference); if ref_hash.is_pkg_file() { println!( "{pkg_name} {:04x}/{} 0x{:04x} - Reference {ref_hash:?} / r=0x{:x} (type={}, subtype={}, ext={ext})", tag.pkg_id(), tag.entry_index(), entry.file_size, ref_hash.0, entry.file_type, entry.file_subtype ); } else { println!( "{pkg_name} {:04x}/{} 0x{:04x} - r=0x{:x} (type={}, subtype={}, ext={ext})", tag.pkg_id(), tag.entry_index(), entry.file_size, ref_hash.0, entry.file_type, entry.file_subtype ); } let data = match package_manager.read_tag(tag) { Ok(data) => data, Err(e) => { eprintln!( "Failed to extract entry {:04x}/{}: {e}", tag.pkg_id(), tag.entry_index() ); continue; } }; let mut o = File::create(format!( "{out_dir}/{tag}_ref-{:08X}_{}_{}.{ext}", entry.reference, entry.file_type, entry.file_subtype ))?; o.write_all(&data)?; } Ok(()) }