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)
}
}
// 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.
use std::{str, fs::File};
use std::io::Read;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use regex::RegexBuilder;
@ -13,7 +13,8 @@ use regex::RegexBuilder;
pub enum WiiFileType {
Wad,
Tmd,
Ticket
Ticket,
U8,
}
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")) {
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() {
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];
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" {
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! ==

View File

@ -4,8 +4,11 @@
// Code for the info command in the rustii CLI.
use std::{str, fs};
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
use anyhow::{bail, Context, Result};
use rustii::archive::u8;
use rustii::{title, title::cert, title::tmd, title::ticket, title::wad, title::versions};
use crate::filetypes::{WiiFileType, identify_file_type};
@ -240,6 +243,35 @@ fn print_wad_info(wad: wad::WAD) -> Result<()> {
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<()> {
let in_path = Path::new(input);
if !in_path.exists() {
@ -247,17 +279,21 @@ pub fn info(input: &str) -> Result<()> {
}
match identify_file_type(input) {
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)?;
},
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)?;
},
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)?;
},
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 => {
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.")?;
println!("Ticket fakesigned!");
},
None => {
_ => {
bail!("You can only fakesign TMDs, Tickets, and WADs!");
}
}