Added basic support for displaying info about U8 archives to rustii CLI

This commit is contained in:
Campbell 2025-04-17 20:33:44 -04:00
parent ea2e31756c
commit 66476e2c98
Signed by: NinjaCheetah
GPG Key ID: 39C2500E1778B156
4 changed files with 60 additions and 23 deletions

View File

@ -345,19 +345,3 @@ impl U8Archive {
Ok(buf) Ok(buf)
} }
} }
// pub fn print_full_tree(dir: &Rc<RefCell<U8Directory>>, indent: usize) {
// let prefix = " ".repeat(indent);
// println!("{}D {}", prefix, dir.borrow().name);
//
// // Print subdirectories
// for subdir in &dir.borrow().dirs {
// print_full_tree(subdir, indent + 1);
// }
//
// // Print files
// for file in &dir.borrow().files {
// let file_name = &file.borrow().name;
// println!("{} F {}", prefix, file_name);
// }
// }

View File

@ -4,7 +4,7 @@
// Common code for identifying Wii file types. // Common code for identifying Wii file types.
use std::{str, fs::File}; use std::{str, fs::File};
use std::io::Read; use std::io::{Read, Seek, SeekFrom};
use std::path::Path; use std::path::Path;
use regex::RegexBuilder; use regex::RegexBuilder;
@ -13,7 +13,8 @@ use regex::RegexBuilder;
pub enum WiiFileType { pub enum WiiFileType {
Wad, Wad,
Tmd, Tmd,
Ticket Ticket,
U8,
} }
pub fn identify_file_type(input: &str) -> Option<WiiFileType> { pub fn identify_file_type(input: &str) -> Option<WiiFileType> {
@ -35,14 +36,30 @@ pub fn identify_file_type(input: &str) -> Option<WiiFileType> {
if input.extension().is_some_and(|f| f.eq_ignore_ascii_case("wad")) { if input.extension().is_some_and(|f| f.eq_ignore_ascii_case("wad")) {
return Some(WiiFileType::Wad); return Some(WiiFileType::Wad);
} }
// Advanced WAD detection, where we read and compare the first 8 bytes (only if the path exists.) // == U8 ==
if input.extension().is_some_and(|f| f.eq_ignore_ascii_case("arc")) ||
input.extension().is_some_and(|f| f.eq_ignore_ascii_case("app")) {
return Some(WiiFileType::U8);
}
// == Advanced ==
// These require reading the magic number of the file, so we only try this after everything
// else has been tried. These are separated from the other methods of detecting these types so
// that we only have to open the file for reading once.
if input.exists() { if input.exists() {
let mut f = File::open(input).unwrap(); let mut f = File::open(input).unwrap();
// We need to read more bytes for WADs since they don't have a proper magic number.
let mut magic_number = vec![0u8; 8]; let mut magic_number = vec![0u8; 8];
f.read_exact(&mut magic_number).unwrap(); f.read_exact(&mut magic_number).unwrap();
if magic_number == b"\x00\x00\x00\x20\x49\x73\x00\x00" || magic_number == b"\x00\x00\x00\x20\x69\x62\x00\x00" { if magic_number == b"\x00\x00\x00\x20\x49\x73\x00\x00" || magic_number == b"\x00\x00\x00\x20\x69\x62\x00\x00" {
return Some(WiiFileType::Wad); return Some(WiiFileType::Wad);
} }
let mut magic_number = vec![0u8; 4];
f.seek(SeekFrom::Start(0)).unwrap();
f.read_exact(&mut magic_number).unwrap();
if magic_number == b"\x55\xAA\x38\x2D" {
return Some(WiiFileType::U8);
}
} }
// == No match found! == // == No match found! ==

View File

@ -4,8 +4,11 @@
// Code for the info command in the rustii CLI. // Code for the info command in the rustii CLI.
use std::{str, fs}; use std::{str, fs};
use std::cell::RefCell;
use std::path::Path; use std::path::Path;
use std::rc::Rc;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use rustii::archive::u8;
use rustii::{title, title::cert, title::tmd, title::ticket, title::wad, title::versions}; use rustii::{title, title::cert, title::tmd, title::ticket, title::wad, title::versions};
use crate::filetypes::{WiiFileType, identify_file_type}; use crate::filetypes::{WiiFileType, identify_file_type};
@ -240,6 +243,35 @@ fn print_wad_info(wad: wad::WAD) -> Result<()> {
Ok(()) Ok(())
} }
fn print_full_tree(dir: &Rc<RefCell<u8::U8Directory>>, indent: usize) {
let prefix = " ".repeat(indent);
let dir_name = if !dir.borrow().name.is_empty() {
&dir.borrow().name
} else {
&String::from("root")
};
println!("{}D {}", prefix, dir_name);
// Print subdirectories
for subdir in &dir.borrow().dirs {
print_full_tree(subdir, indent + 1);
}
// Print files
for file in &dir.borrow().files {
let file_name = &file.borrow().name;
println!("{} F {}", prefix, file_name);
}
}
fn print_u8_info(u8_archive: u8::U8Archive) -> Result<()> {
println!("U8 Archive Info");
println!(" Node Count: {}", u8_archive.node_tree.borrow().count());
println!(" Archive Data:");
print_full_tree(&u8_archive.node_tree, 2);
Ok(())
}
pub fn info(input: &str) -> Result<()> { pub fn info(input: &str) -> Result<()> {
let in_path = Path::new(input); let in_path = Path::new(input);
if !in_path.exists() { if !in_path.exists() {
@ -247,17 +279,21 @@ pub fn info(input: &str) -> Result<()> {
} }
match identify_file_type(input) { match identify_file_type(input) {
Some(WiiFileType::Tmd) => { Some(WiiFileType::Tmd) => {
let tmd = tmd::TMD::from_bytes(fs::read(in_path)?.as_slice()).with_context(|| "The provided TMD file could not be parsed, and is likely invalid.")?; let tmd = tmd::TMD::from_bytes(&fs::read(in_path)?).with_context(|| "The provided TMD file could not be parsed, and is likely invalid.")?;
print_tmd_info(tmd, None)?; print_tmd_info(tmd, None)?;
}, },
Some(WiiFileType::Ticket) => { Some(WiiFileType::Ticket) => {
let ticket = ticket::Ticket::from_bytes(fs::read(in_path)?.as_slice()).with_context(|| "The provided Ticket file could not be parsed, and is likely invalid.")?; let ticket = ticket::Ticket::from_bytes(&fs::read(in_path)?).with_context(|| "The provided Ticket file could not be parsed, and is likely invalid.")?;
print_ticket_info(ticket, None)?; print_ticket_info(ticket, None)?;
}, },
Some(WiiFileType::Wad) => { Some(WiiFileType::Wad) => {
let wad = wad::WAD::from_bytes(fs::read(in_path)?.as_slice()).with_context(|| "The provided WAD file could not be parsed, and is likely invalid.")?; let wad = wad::WAD::from_bytes(&fs::read(in_path)?).with_context(|| "The provided WAD file could not be parsed, and is likely invalid.")?;
print_wad_info(wad)?; print_wad_info(wad)?;
}, },
Some(WiiFileType::U8) => {
let u8_archive = u8::U8Archive::from_bytes(&fs::read(in_path)?).with_context(|| "The provided U8 archive could not be parsed, and is likely invalid.")?;
print_u8_info(u8_archive)?;
}
None => { None => {
bail!("Information cannot be displayed for this file type."); bail!("Information cannot be displayed for this file type.");
} }

View File

@ -57,7 +57,7 @@ pub fn fakesign(input: &str, output: &Option<String>) -> Result<()> {
fs::write(out_path, ticket.to_bytes()?).with_context(|| "Could not open output file for writing.")?; fs::write(out_path, ticket.to_bytes()?).with_context(|| "Could not open output file for writing.")?;
println!("Ticket fakesigned!"); println!("Ticket fakesigned!");
}, },
None => { _ => {
bail!("You can only fakesign TMDs, Tickets, and WADs!"); bail!("You can only fakesign TMDs, Tickets, and WADs!");
} }
} }