mirror of
https://github.com/NinjaCheetah/rustii.git
synced 2025-06-05 23:11:02 -04:00
Add WIP info command to rustii CLI, currently has a lot of blanks
This commit is contained in:
parent
839e33b911
commit
8c7cd48dff
140
src/bin/rustii/info.rs
Normal file
140
src/bin/rustii/info.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// info.rs from rustii (c) 2025 NinjaCheetah & Contributors
|
||||||
|
// https://github.com/NinjaCheetah/rustii
|
||||||
|
//
|
||||||
|
// Code for the info command in the rustii CLI.
|
||||||
|
|
||||||
|
use std::{str, fs};
|
||||||
|
use std::path::Path;
|
||||||
|
use rustii::{title, title::tmd, title::ticket, title::wad};
|
||||||
|
use crate::filetypes::{WiiFileType, identify_file_type};
|
||||||
|
|
||||||
|
fn print_tmd_info(tmd: tmd::TMD) {
|
||||||
|
// Print all important keys from the TMD.
|
||||||
|
println!("Title Info");
|
||||||
|
println!(" Title ID: {}", hex::encode(tmd.title_id));
|
||||||
|
println!(" Title Version: {}", tmd.title_version);
|
||||||
|
println!(" TMD Version: {}", tmd.tmd_version);
|
||||||
|
if hex::encode(tmd.ios_tid) == "0000000000000000" {
|
||||||
|
println!(" Required IOS: N/A");
|
||||||
|
}
|
||||||
|
else if hex::encode(tmd.ios_tid) != "0000000100000001" {
|
||||||
|
println!(" Required IOS: IOS{} ({})", tmd.ios_tid.last().unwrap(), hex::encode(tmd.ios_tid));
|
||||||
|
}
|
||||||
|
let signature_issuer = String::from_utf8(Vec::from(tmd.signature_issuer)).unwrap_or_default();
|
||||||
|
if signature_issuer.contains("CP00000004") {
|
||||||
|
println!(" Certificate: CP00000004 (Retail)");
|
||||||
|
println!(" Certificate Issuer: Root-CA00000001 (Retail)");
|
||||||
|
}
|
||||||
|
else if signature_issuer.contains("CP00000007") {
|
||||||
|
println!(" Certificate: CP00000007 (Development)");
|
||||||
|
println!(" Certificate Issuer: Root-CA00000002 (Development)");
|
||||||
|
}
|
||||||
|
else if signature_issuer.contains("CP00000005") {
|
||||||
|
println!(" Certificate: CP00000005 (Development/Unknown)");
|
||||||
|
println!(" Certificate Issuer: Root-CA00000002 (Development)");
|
||||||
|
}
|
||||||
|
else if signature_issuer.contains("CP10000000") {
|
||||||
|
println!(" Certificate: CP10000000 (Arcade)");
|
||||||
|
println!(" Certificate Issuer: Root-CA10000000 (Arcade)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println!(" Certificate Info: {} (Unknown)", signature_issuer);
|
||||||
|
}
|
||||||
|
println!(" Region: WIP");
|
||||||
|
println!(" Title Type: WIP");
|
||||||
|
println!(" vWii Title: {}", tmd.is_vwii != 0);
|
||||||
|
println!(" DVD Video Access: WIP");
|
||||||
|
println!(" AHB Access: WIP");
|
||||||
|
println!(" Fakesigned: {}", tmd.is_fakesigned());
|
||||||
|
println!("\nContent Info");
|
||||||
|
println!(" Total Contents: {}", tmd.num_contents);
|
||||||
|
println!(" Boot Content Index: {}", tmd.boot_index);
|
||||||
|
println!(" Content Records:");
|
||||||
|
for content in tmd.content_records {
|
||||||
|
println!(" Content Index: {}", content.index);
|
||||||
|
println!(" Content ID: {:08X}", content.content_id);
|
||||||
|
println!(" Content Type: WIP");
|
||||||
|
println!(" Content Size: {} bytes", content.content_size);
|
||||||
|
println!(" Content Hash: {}", hex::encode(content.content_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_ticket_info(ticket: ticket::Ticket) {
|
||||||
|
// Print all important keys from the Ticket.
|
||||||
|
println!("Ticket Info");
|
||||||
|
println!(" Title ID: {}", hex::encode(ticket.title_id));
|
||||||
|
println!(" Title Version: {}", ticket.title_version);
|
||||||
|
println!(" Ticket Version: {}", ticket.ticket_version);
|
||||||
|
let signature_issuer = String::from_utf8(Vec::from(ticket.signature_issuer)).unwrap_or_default();
|
||||||
|
if signature_issuer.contains("XS00000003") {
|
||||||
|
println!(" Certificate: XS00000003 (Retail)");
|
||||||
|
println!(" Certificate Issuer: Root-CA00000001 (Retail)");
|
||||||
|
}
|
||||||
|
else if signature_issuer.contains("XS00000006") {
|
||||||
|
println!(" Certificate: XS00000006 (Development)");
|
||||||
|
println!(" Certificate Issuer: Root-CA00000002 (Development)");
|
||||||
|
}
|
||||||
|
else if signature_issuer.contains("XS00000004") {
|
||||||
|
println!(" Certificate: XS00000004 (Development/Unknown)");
|
||||||
|
println!(" Certificate Issuer: Root-CA00000002 (Development)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println!(" Certificate Info: {} (Unknown)", signature_issuer);
|
||||||
|
}
|
||||||
|
let key = match ticket.common_key_index {
|
||||||
|
0 => {
|
||||||
|
if ticket.is_dev() { "Common (Development)" }
|
||||||
|
else { "Common (Retail)" }
|
||||||
|
}
|
||||||
|
1 => "Korean",
|
||||||
|
2 => "vWii",
|
||||||
|
_ => "Unknown (Likely Common)"
|
||||||
|
};
|
||||||
|
println!(" Decryption Key: {}", key);
|
||||||
|
println!(" Title Key (Encrypted): {}", hex::encode(ticket.title_key));
|
||||||
|
println!(" Title Key (Decrypted): {}", hex::encode(ticket.dec_title_key()));
|
||||||
|
println!(" Fakesigned: {}", ticket.is_fakesigned());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_wad_info(wad: wad::WAD) {
|
||||||
|
println!("WAD Info");
|
||||||
|
match wad.header.wad_type {
|
||||||
|
wad::WADType::ImportBoot => { println!(" WAD Type: boot2") },
|
||||||
|
wad::WADType::Installable => { println!(" WAD Type: Standard Installable") },
|
||||||
|
}
|
||||||
|
println!(" Installed Size: WIP blocks");
|
||||||
|
println!(" Installed Size (MB): WIP MB");
|
||||||
|
println!(" Has Meta/Footer: {}", wad.meta_size() != 0);
|
||||||
|
println!(" Has CRL: {}", wad.crl_size() != 0);
|
||||||
|
// Create a Title for signing info and TMD/Ticket info.
|
||||||
|
let title = title::Title::from_wad(&wad).unwrap();
|
||||||
|
println!(" Fakesigned: {}", title.is_fakesigned());
|
||||||
|
println!();
|
||||||
|
print_ticket_info(title.ticket);
|
||||||
|
println!();
|
||||||
|
print_tmd_info(title.tmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn info(input: &str) {
|
||||||
|
let in_path = Path::new(input);
|
||||||
|
if !in_path.exists() {
|
||||||
|
panic!("Error: Input file does not exist.");
|
||||||
|
}
|
||||||
|
match identify_file_type(input) {
|
||||||
|
Some(WiiFileType::Tmd) => {
|
||||||
|
let tmd = tmd::TMD::from_bytes(fs::read(in_path).unwrap().as_slice()).unwrap();
|
||||||
|
print_tmd_info(tmd);
|
||||||
|
},
|
||||||
|
Some(WiiFileType::Ticket) => {
|
||||||
|
let ticket = ticket::Ticket::from_bytes(fs::read(in_path).unwrap().as_slice()).unwrap();
|
||||||
|
print_ticket_info(ticket);
|
||||||
|
},
|
||||||
|
Some(WiiFileType::Wad) => {
|
||||||
|
let wad = wad::WAD::from_bytes(fs::read(in_path).unwrap().as_slice()).unwrap();
|
||||||
|
print_wad_info(wad);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
println!("Error: Information cannot be displayed for this file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
mod title;
|
mod title;
|
||||||
mod filetypes;
|
mod filetypes;
|
||||||
|
mod info;
|
||||||
|
|
||||||
use clap::{Subcommand, Parser};
|
use clap::{Subcommand, Parser};
|
||||||
use title::{wad, fakesign};
|
use title::{wad, fakesign};
|
||||||
@ -26,10 +27,16 @@ enum Commands {
|
|||||||
},
|
},
|
||||||
/// Fakesign a TMD, Ticket, or WAD (trucha bug)
|
/// Fakesign a TMD, Ticket, or WAD (trucha bug)
|
||||||
Fakesign {
|
Fakesign {
|
||||||
/// Fakesign a TMD, Ticket, or WAD (trucha bug)
|
/// The path to a TMD, Ticket, or WAD
|
||||||
input: String,
|
input: String,
|
||||||
|
/// An (optional) output name; defaults to overwriting input file if not provided
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
output: Option<String>,
|
output: Option<String>,
|
||||||
|
},
|
||||||
|
/// Get information about a TMD, Ticket, or WAD
|
||||||
|
Info {
|
||||||
|
/// The path to a TMD, Ticket, or WAD
|
||||||
|
input: String,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,11 +52,14 @@ fn main() {
|
|||||||
Some(wad::Commands::Unpack { input, output }) => {
|
Some(wad::Commands::Unpack { input, output }) => {
|
||||||
wad::unpack_wad(input, output)
|
wad::unpack_wad(input, output)
|
||||||
},
|
},
|
||||||
&None => { /* This is handled by clap */}
|
&None => { /* This is for me handled by clap */}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(Commands::Fakesign { input, output }) => {
|
Some(Commands::Fakesign { input, output }) => {
|
||||||
fakesign::fakesign(input, output)
|
fakesign::fakesign(input, output)
|
||||||
|
},
|
||||||
|
Some(Commands::Info { input }) => {
|
||||||
|
info::info(input)
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// title/fakesign.rs from rustii (c) 2025 NinjaCheetah & Contributors
|
// title/fakesign.rs from rustii (c) 2025 NinjaCheetah & Contributors
|
||||||
// https://github.com/NinjaCheetah/rustii
|
// https://github.com/NinjaCheetah/rustii
|
||||||
//
|
//
|
||||||
// Code for dedicated fakesigning-related commands in the rustii CLI.
|
// Code for the fakesign command in the rustii CLI.
|
||||||
|
|
||||||
use std::{str, fs};
|
use std::{str, fs};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use crate::filetypes::{WiiFileType, identify_file_type};
|
|
||||||
use rustii::{title, title::tmd, title::ticket};
|
use rustii::{title, title::tmd, title::ticket};
|
||||||
|
use crate::filetypes::{WiiFileType, identify_file_type};
|
||||||
|
|
||||||
pub fn fakesign(input: &str, output: &Option<String>) {
|
pub fn fakesign(input: &str, output: &Option<String>) {
|
||||||
let in_path = Path::new(input);
|
let in_path = Path::new(input);
|
||||||
|
@ -210,7 +210,7 @@ impl Ticket {
|
|||||||
let mut current_int: u16 = 0;
|
let mut current_int: u16 = 0;
|
||||||
let mut test_hash: [u8; 20] = [255; 20];
|
let mut test_hash: [u8; 20] = [255; 20];
|
||||||
while test_hash[0] != 0 {
|
while test_hash[0] != 0 {
|
||||||
if current_int == 255 { return Err(TicketError::CannotFakesign); }
|
if current_int == 65535 { return Err(TicketError::CannotFakesign); }
|
||||||
current_int += 1;
|
current_int += 1;
|
||||||
self.unknown2 = current_int.to_be_bytes();
|
self.unknown2 = current_int.to_be_bytes();
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
|
@ -211,7 +211,7 @@ impl TMD {
|
|||||||
let mut current_int: u16 = 0;
|
let mut current_int: u16 = 0;
|
||||||
let mut test_hash: [u8; 20] = [255; 20];
|
let mut test_hash: [u8; 20] = [255; 20];
|
||||||
while test_hash[0] != 0 {
|
while test_hash[0] != 0 {
|
||||||
if current_int == 255 { return Err(TMDError::CannotFakesign); }
|
if current_int == 65535 { return Err(TMDError::CannotFakesign); }
|
||||||
current_int += 1;
|
current_int += 1;
|
||||||
self.minor_version = current_int;
|
self.minor_version = current_int;
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
|
@ -238,6 +238,8 @@ impl WAD {
|
|||||||
buf.resize((buf.len() + 63) & !63, 0);
|
buf.resize((buf.len() + 63) & !63, 0);
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cert_chain_size(&self) -> u32 { self.header.cert_chain_size }
|
||||||
|
|
||||||
pub fn cert_chain(&self) -> Vec<u8> {
|
pub fn cert_chain(&self) -> Vec<u8> {
|
||||||
self.body.cert_chain.clone()
|
self.body.cert_chain.clone()
|
||||||
@ -247,6 +249,8 @@ impl WAD {
|
|||||||
self.body.cert_chain = cert_chain.to_vec();
|
self.body.cert_chain = cert_chain.to_vec();
|
||||||
self.header.cert_chain_size = cert_chain.len() as u32;
|
self.header.cert_chain_size = cert_chain.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn crl_size(&self) -> u32 { self.header.crl_size }
|
||||||
|
|
||||||
pub fn crl(&self) -> Vec<u8> {
|
pub fn crl(&self) -> Vec<u8> {
|
||||||
self.body.crl.clone()
|
self.body.crl.clone()
|
||||||
@ -256,6 +260,8 @@ impl WAD {
|
|||||||
self.body.crl = crl.to_vec();
|
self.body.crl = crl.to_vec();
|
||||||
self.header.crl_size = crl.len() as u32;
|
self.header.crl_size = crl.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ticket_size(&self) -> u32 { self.header.ticket_size }
|
||||||
|
|
||||||
pub fn ticket(&self) -> Vec<u8> {
|
pub fn ticket(&self) -> Vec<u8> {
|
||||||
self.body.ticket.clone()
|
self.body.ticket.clone()
|
||||||
@ -265,6 +271,8 @@ impl WAD {
|
|||||||
self.body.ticket = ticket.to_vec();
|
self.body.ticket = ticket.to_vec();
|
||||||
self.header.ticket_size = ticket.len() as u32;
|
self.header.ticket_size = ticket.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tmd_size(&self) -> u32 { self.header.tmd_size }
|
||||||
|
|
||||||
pub fn tmd(&self) -> Vec<u8> {
|
pub fn tmd(&self) -> Vec<u8> {
|
||||||
self.body.tmd.clone()
|
self.body.tmd.clone()
|
||||||
@ -274,6 +282,8 @@ impl WAD {
|
|||||||
self.body.tmd = tmd.to_vec();
|
self.body.tmd = tmd.to_vec();
|
||||||
self.header.tmd_size = tmd.len() as u32;
|
self.header.tmd_size = tmd.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn content_size(&self) -> u32 { self.header.content_size }
|
||||||
|
|
||||||
pub fn content(&self) -> Vec<u8> {
|
pub fn content(&self) -> Vec<u8> {
|
||||||
self.body.content.clone()
|
self.body.content.clone()
|
||||||
@ -283,6 +293,8 @@ impl WAD {
|
|||||||
self.body.content = content.to_vec();
|
self.body.content = content.to_vec();
|
||||||
self.header.content_size = content.len() as u32;
|
self.header.content_size = content.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn meta_size(&self) -> u32 { self.header.meta_size }
|
||||||
|
|
||||||
pub fn meta(&self) -> Vec<u8> {
|
pub fn meta(&self) -> Vec<u8> {
|
||||||
self.body.meta.clone()
|
self.body.meta.clone()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user