diff --git a/src/command/pack.rs b/src/command/pack.rs index ab6c9b2..bc48640 100644 --- a/src/command/pack.rs +++ b/src/command/pack.rs @@ -1,13 +1,13 @@ use std::ffi::OsStr; use std::path::PathBuf; -use anyhow::Context; +use anyhow::Context; use walkdir::WalkDir; -use crate::{crypto, I18nCompatMode, NewArgs}; use crate::command::AssetMetadata; +use crate::crypto; -pub fn pack(args: &NewArgs, input: &Option, output: &PathBuf, locale_mode: &I18nCompatMode) -> anyhow::Result<()> { +pub fn pack(art_key: &String, input: &Option, output: &PathBuf) -> anyhow::Result<()> { let input = find_input(input); if let Err(e) = input { anyhow::bail!("Error while finding input: {}", e); @@ -17,7 +17,7 @@ pub fn pack(args: &NewArgs, input: &Option, output: &PathBuf, locale_mo match extension { Some(ext) => { if ext == OsStr::new("dat") || ext == OsStr::new("txt") { - pack_dat(args, &input.unwrap(), output, locale_mode) + pack_dat(art_key, &input.unwrap(), output) } else { anyhow::bail!("Output file has an invalid extension. (Use .dat or .txt)"); } @@ -62,7 +62,7 @@ fn find_input(input: &Option) -> anyhow::Result { } } -fn pack_dat(args: &NewArgs, input: &PathBuf, output: &PathBuf, _locale_mode: &I18nCompatMode) -> anyhow::Result<()> { +fn pack_dat(art_key: &String, input: &PathBuf, output: &PathBuf) -> anyhow::Result<()> { let mut assets: Vec = Vec::new(); let mut asset_bytes: Vec = Vec::new(); @@ -100,7 +100,7 @@ fn pack_dat(args: &NewArgs, input: &PathBuf, output: &PathBuf, _locale_mode: &I1 out.append(&mut asset_bytes); println!("Encrypting assets..."); - let key = args.art_key.clone().unwrap(); + let key = art_key.clone(); let enc_key = crypto::to_key_array(key.as_str()); let enc_key = enc_key.as_slice(); crypto::encrypt(enc_key, out.as_mut_slice()); diff --git a/src/command/unpack.rs b/src/command/unpack.rs index 55e81b9..240ab06 100644 --- a/src/command/unpack.rs +++ b/src/command/unpack.rs @@ -9,7 +9,7 @@ use binrw::io::BufReader; use crate::{crypto, NewArgs}; use crate::command::ArtHeader; -use crate::read_ext::ReadExt; +use crate::io_ext::ReadExt; use crate::unity::AssetsFile; pub fn unpack(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyhow::Result<()> { @@ -17,9 +17,9 @@ pub fn unpack(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyhow::Resu match extension { Some(ext) => { if ext == OsStr::new("dat") || ext == OsStr::new("txt") { - unpack_dat(args, input, output) + unpack_dat(args, input, output)?; } else if ext == OsStr::new("assets") { - unpack_assets(args, input, output) + unpack_assets(args, input, output)?; } else { anyhow::bail!("Input file has an invalid extension. (Supported: .dat, .assets)"); } @@ -28,6 +28,8 @@ pub fn unpack(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyhow::Resu anyhow::bail!("Input file has no extension. (Supported: .dat, .assets)"); } } + + Ok(()) } pub fn unpack_dat(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyhow::Result<()> { @@ -38,8 +40,8 @@ pub fn unpack_dat(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyhow:: // key can be unwrapped safely here let key = args.art_key.clone().unwrap(); let enc_key = crypto::to_key_array(key.as_str()); - let enc_key = enc_key.as_slice(); - crypto::decrypt(enc_key, data.as_mut_slice()); + let enc_key_slice = enc_key.as_slice(); + crypto::decrypt(enc_key_slice, data.as_mut_slice()); // Read header string let len = u16::from_le_bytes([data[0], data[1]]) as usize; @@ -76,8 +78,15 @@ pub fn unpack_dat(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyhow:: Ok(()) } -pub fn unpack_assets(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyhow::Result<()> { - let input = File::open(input) +pub struct RepackInfo { + pub assets: AssetsFile, + pub art_path_id: i64, + pub art_key: String, + pub original_assets: PathBuf, +} + +pub fn unpack_assets(args: &NewArgs, input_path: &PathBuf, output: &PathBuf) -> anyhow::Result { + let input = File::open(input_path) .context("Failed to open input file")?; let mut input = BufReader::new(input); let assets = AssetsFile::read(&mut input) @@ -86,11 +95,12 @@ pub fn unpack_assets(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyho .context("Failed to resolve object classes")?; let mut art_file: Option = None; + let mut art_path_id: Option = None; for obj in objects { if obj.class_id == 49 { // text asset input.seek(SeekFrom::Start(assets.header.offset_first_file + obj.byte_start)) .context("Failed to seek to object")?; - let name = input.read_dyn_string(&assets.header.endianness, i32::BITS) + let name = input.read_dyn_string(&assets.header.endianness) .context("Failed to read object name")?; if name == "Art.dat" { @@ -110,8 +120,9 @@ pub fn unpack_assets(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyho std::io::copy(&mut temp_reader, &mut temp_writer) .context("Failed to copy object data")?; - + art_file = Some(temp); + art_path_id = Some(obj.path_id); break; } } @@ -123,9 +134,14 @@ pub fn unpack_assets(args: &NewArgs, input: &PathBuf, output: &PathBuf) -> anyho if let Err(e) = std::fs::remove_file(art_file) { eprintln!("Failed to remove temporary file: {}", e); } + // Any unwraps here are safe because None values would've resulted in earlier bail + Ok(RepackInfo { + assets, + art_path_id: art_path_id.unwrap(), + art_key: args.art_key.clone().unwrap(), + original_assets: input_path.clone(), + }) } else { anyhow::bail!("Failed to find Art.dat object in assets file"); } - - Ok(()) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 4be3ac6..97e8162 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use clap_derive::{Parser, Subcommand, ValueEnum}; use crate::command::{pack, patch, unpack}; mod crypto; -mod read_ext; +mod io_ext; mod command; mod unity; @@ -57,10 +57,6 @@ enum Command { /// Output file. Make sure to use the .dat or .txt extension. #[arg(short, long, default_value = "Art-modded.dat")] output: PathBuf, - - /// How should the tool handle localized assets. - #[arg(long, default_value = "none")] - i18n: I18nCompatMode, }, /// Unpack assets from an Art.dat or unity asset bundle. Unpack { @@ -108,8 +104,9 @@ fn main() { } let res = match &args.command { - Command::Pack { input, output, i18n } => { - pack::pack(&args, input, output, i18n) + Command::Pack { input, output } => { + // unwrap is safe here + pack::pack(&args.art_key.unwrap(), input, output) } Command::Unpack { input, output } => { unpack::unpack(&args, input, output) diff --git a/src/unity/mod.rs b/src/unity/mod.rs index 3f993d8..fd40dba 100644 --- a/src/unity/mod.rs +++ b/src/unity/mod.rs @@ -9,7 +9,7 @@ pub mod util; #[derive(Debug, PartialEq)] pub struct AssetsFile { #[brw(big)] - pub header: AssetFileHeader, + pub header: AssetsFileHeader, #[brw(is_little = header.endianness == Endian::Little)] pub content: AssetsFileContent, } @@ -38,7 +38,7 @@ impl AssetsFile { #[binrw] #[brw(big)] #[derive(Debug, PartialEq)] -pub struct AssetFileHeader { +pub struct AssetsFileHeader { #[br(assert(version == 22))] #[brw(pad_before = 8, pad_after = 4)] pub version: u32,