mirror of
https://github.com/NinjaCheetah/rustii.git
synced 2026-03-04 11:55:27 -05:00
Redesigned how U8 archives are represented in memory
This replaces the old 1D array with an actual directory tree that can be used to make packing, unpacking, and editing U8 archives much much easier than the old libWiiPy implementation.
This commit is contained in:
@@ -14,31 +14,35 @@ fn main() {
|
||||
println!("num content records: {:?}", title.tmd.content_records.len());
|
||||
println!("first record data: {:?}", title.tmd.content_records.first().unwrap());
|
||||
println!("TMD is fakesigned: {:?}",title.tmd.is_fakesigned());
|
||||
|
||||
|
||||
println!("title version from ticket is: {:?}", title.ticket.title_version);
|
||||
println!("title key (enc): {:?}", title.ticket.title_key);
|
||||
println!("title key (dec): {:?}", title.ticket.dec_title_key());
|
||||
println!("ticket is fakesigned: {:?}", title.ticket.is_fakesigned());
|
||||
|
||||
println!("title is fakesigned: {:?}", title.is_fakesigned());
|
||||
|
||||
|
||||
println!("wad header: {:?}", wad.header);
|
||||
|
||||
let cert_chain = &title.cert_chain;
|
||||
println!("cert chain OK");
|
||||
let result = cert::verify_ca_cert(&cert_chain.ca_cert()).unwrap();
|
||||
println!("CA cert {} verified successfully: {}", cert_chain.ca_cert().child_cert_identity(), result);
|
||||
|
||||
|
||||
let result = cert::verify_child_cert(&cert_chain.ca_cert(), &cert_chain.tmd_cert()).unwrap();
|
||||
println!("TMD cert {} verified successfully: {}", cert_chain.tmd_cert().child_cert_identity(), result);
|
||||
let result = cert::verify_tmd(&cert_chain.tmd_cert(), &title.tmd).unwrap();
|
||||
println!("TMD verified successfully: {}", result);
|
||||
|
||||
|
||||
let result = cert::verify_child_cert(&cert_chain.ca_cert(), &cert_chain.ticket_cert()).unwrap();
|
||||
println!("Ticket cert {} verified successfully: {}", cert_chain.ticket_cert().child_cert_identity(), result);
|
||||
let result = cert::verify_ticket(&cert_chain.ticket_cert(), &title.ticket).unwrap();
|
||||
println!("Ticket verified successfully: {}", result);
|
||||
|
||||
|
||||
let result = title.verify().unwrap();
|
||||
println!("full title verified successfully: {}", result);
|
||||
// let mut u8_archive = u8::U8Archive::from_bytes(&fs::read("00000001.app").unwrap()).unwrap();
|
||||
// println!("files and dirs counted: {}", u8_archive.node_tree.borrow().count());
|
||||
// fs::write("outfile.arc", u8_archive.to_bytes().unwrap()).unwrap();
|
||||
// println!("re-written");
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn decompress_ash(input: &str, output: &Option<String>) -> Result<()> {
|
||||
let out_path = if output.is_some() {
|
||||
PathBuf::from(output.clone().unwrap())
|
||||
} else {
|
||||
PathBuf::from(in_path).with_extension(format!("{}.out", in_path.extension().unwrap_or("".as_ref()).to_str().unwrap()))
|
||||
PathBuf::from(in_path.file_name().unwrap()).with_extension(format!("{}.out", in_path.extension().unwrap_or("".as_ref()).to_str().unwrap()))
|
||||
};
|
||||
fs::write(out_path.clone(), decompressed)?;
|
||||
println!("Successfully decompressed ASH file to \"{}\"!", out_path.display());
|
||||
|
||||
@@ -57,7 +57,7 @@ pub fn decompress_lz77(input: &str, output: &Option<String>) -> Result<()> {
|
||||
let out_path = if output.is_some() {
|
||||
PathBuf::from(output.clone().unwrap())
|
||||
} else {
|
||||
PathBuf::from(in_path).with_extension(format!("{}.out", in_path.extension().unwrap_or("".as_ref()).to_str().unwrap()))
|
||||
PathBuf::from(in_path.file_name().unwrap()).with_extension(format!("{}.out", in_path.extension().unwrap_or("".as_ref()).to_str().unwrap()))
|
||||
};
|
||||
fs::write(out_path.clone(), decompressed)?;
|
||||
println!("Successfully decompressed LZ77 file to \"{}\"!", out_path.display());
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
// Code for the U8 packing/unpacking commands in the rustii CLI.
|
||||
|
||||
use std::{str, fs};
|
||||
use std::cell::RefCell;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::Subcommand;
|
||||
use rustii::archive::u8;
|
||||
@@ -32,6 +34,20 @@ pub fn pack_u8_archive(_input: &str, _output: &str) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn unpack_dir_recursive(dir: &Rc<RefCell<u8::U8Directory>>, out_path: PathBuf) -> Result<()> {
|
||||
let out_path = out_path.join(&dir.borrow().name);
|
||||
for file in &dir.borrow().files {
|
||||
fs::write(out_path.join(&file.borrow().name), &file.borrow().data).with_context(|| format!("Failed to write output file \"{}\".", &file.borrow().name))?;
|
||||
}
|
||||
for dir in &dir.borrow().dirs {
|
||||
if !out_path.join(&dir.borrow().name).exists() {
|
||||
fs::create_dir(out_path.join(&dir.borrow().name)).with_context(|| format!("The output directory \"{}\" could not be created.", out_path.display()))?;
|
||||
}
|
||||
unpack_dir_recursive(dir, out_path.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpack_u8_archive(input: &str, output: &str) -> Result<()> {
|
||||
let in_path = Path::new(input);
|
||||
if !in_path.exists() {
|
||||
@@ -45,51 +61,10 @@ pub fn unpack_u8_archive(input: &str, output: &str) -> Result<()> {
|
||||
} else {
|
||||
fs::create_dir(&out_path).with_context(|| format!("The output directory \"{}\" could not be created.", out_path.display()))?;
|
||||
}
|
||||
let u8_archive = u8::U8Archive::from_bytes(&fs::read(in_path).with_context(|| format!("Failed to open U8 archive \"{}\" for reading.", in_path.display()))?)?;
|
||||
// This stores the path we're actively writing files to.
|
||||
let mut current_dir = out_path.clone();
|
||||
// This is the order of directory nodes we've traversed down.
|
||||
let mut parent_dirs: Vec<u32> = Vec::from([0]);
|
||||
for i in 0..u8_archive.u8_nodes.len() {
|
||||
match u8_archive.u8_nodes[i].node_type {
|
||||
1 => {
|
||||
// Code for a directory node.
|
||||
if u8_archive.u8_nodes[i].name_offset != 0 {
|
||||
// If we're already at the correct level, make a new directory and push it to
|
||||
// the parent_dirs vec.
|
||||
if u8_archive.u8_nodes[i].data_offset == *parent_dirs.last().unwrap() {
|
||||
current_dir = current_dir.join(&u8_archive.file_names[i]);
|
||||
if !current_dir.exists() {
|
||||
fs::create_dir(¤t_dir).with_context(|| format!("Failed to create directory \"{}\".", current_dir.display()))?;
|
||||
}
|
||||
parent_dirs.push(i as u32);
|
||||
}
|
||||
// Otherwise, go back up the path until we're at the correct level.
|
||||
else {
|
||||
while u8_archive.u8_nodes[i].data_offset != *parent_dirs.last().unwrap() {
|
||||
parent_dirs.pop();
|
||||
}
|
||||
parent_dirs.push(i as u32);
|
||||
current_dir = out_path.clone();
|
||||
// Rebuild current working directory, and make sure all directories in the
|
||||
// path exist.
|
||||
for dir in &parent_dirs {
|
||||
current_dir = current_dir.join(&u8_archive.file_names[*dir as usize]);
|
||||
if !current_dir.exists() {
|
||||
fs::create_dir(¤t_dir).with_context(|| format!("Failed to create directory \"{}\".", current_dir.display()))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
0 => {
|
||||
// Code for a file node.
|
||||
fs::write(current_dir.join(&u8_archive.file_names[i]), &u8_archive.file_data[i])
|
||||
.with_context(|| format!("Failed to write file \"{}\" in directory \"{}\".", u8_archive.file_names[i], current_dir.display()))?;
|
||||
},
|
||||
_ => bail!("Node at index {} has an invalid type! U8 archive cannot be unpacked.", i)
|
||||
}
|
||||
}
|
||||
// Extract the files and directories in the root, and then recurse over each directory to
|
||||
// extract the files and directories they contain.
|
||||
let u8_archive = u8::U8Archive::from_bytes(&fs::read(in_path).with_context(|| format!("Input file \"{}\" could not be read.", in_path.display()))?)?;
|
||||
unpack_dir_recursive(&u8_archive.node_tree, out_path.clone())?;
|
||||
println!("Successfully unpacked U8 archive to directory \"{}\"!", out_path.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user