Filled in many info command blanks

This commit is contained in:
Campbell 2025-03-25 21:06:14 -04:00
parent 8c7cd48dff
commit 3178063a07
Signed by: NinjaCheetah
GPG Key ID: 39C2500E1778B156
2 changed files with 130 additions and 14 deletions

View File

@ -11,14 +11,14 @@ use crate::filetypes::{WiiFileType, identify_file_type};
fn print_tmd_info(tmd: tmd::TMD) { fn print_tmd_info(tmd: tmd::TMD) {
// Print all important keys from the TMD. // Print all important keys from the TMD.
println!("Title Info"); println!("Title Info");
println!(" Title ID: {}", hex::encode(tmd.title_id)); println!(" Title ID: {}", hex::encode(tmd.title_id).to_uppercase());
println!(" Title Version: {}", tmd.title_version); println!(" Title Version: {}", tmd.title_version);
println!(" TMD Version: {}", tmd.tmd_version); println!(" TMD Version: {}", tmd.tmd_version);
if hex::encode(tmd.ios_tid) == "0000000000000000" { if hex::encode(tmd.ios_tid) == "0000000000000000" {
println!(" Required IOS: N/A"); println!(" Required IOS: N/A");
} }
else if hex::encode(tmd.ios_tid) != "0000000100000001" { else if hex::encode(tmd.ios_tid) != "0000000100000001" {
println!(" Required IOS: IOS{} ({})", tmd.ios_tid.last().unwrap(), hex::encode(tmd.ios_tid)); println!(" Required IOS: IOS{} ({})", tmd.ios_tid.last().unwrap(), hex::encode(tmd.ios_tid).to_uppercase());
} }
let signature_issuer = String::from_utf8(Vec::from(tmd.signature_issuer)).unwrap_or_default(); let signature_issuer = String::from_utf8(Vec::from(tmd.signature_issuer)).unwrap_or_default();
if signature_issuer.contains("CP00000004") { if signature_issuer.contains("CP00000004") {
@ -40,11 +40,11 @@ fn print_tmd_info(tmd: tmd::TMD) {
else { else {
println!(" Certificate Info: {} (Unknown)", signature_issuer); println!(" Certificate Info: {} (Unknown)", signature_issuer);
} }
println!(" Region: WIP"); println!(" Region: {}", tmd.region());
println!(" Title Type: WIP"); println!(" Title Type: {}", tmd.title_type());
println!(" vWii Title: {}", tmd.is_vwii != 0); println!(" vWii Title: {}", tmd.is_vwii != 0);
println!(" DVD Video Access: WIP"); println!(" DVD Video Access: {}", tmd.check_access_right(tmd::AccessRight::DVDVideo));
println!(" AHB Access: WIP"); println!(" AHB Access: {}", tmd.check_access_right(tmd::AccessRight::AHB));
println!(" Fakesigned: {}", tmd.is_fakesigned()); println!(" Fakesigned: {}", tmd.is_fakesigned());
println!("\nContent Info"); println!("\nContent Info");
println!(" Total Contents: {}", tmd.num_contents); println!(" Total Contents: {}", tmd.num_contents);
@ -53,7 +53,7 @@ fn print_tmd_info(tmd: tmd::TMD) {
for content in tmd.content_records { for content in tmd.content_records {
println!(" Content Index: {}", content.index); println!(" Content Index: {}", content.index);
println!(" Content ID: {:08X}", content.content_id); println!(" Content ID: {:08X}", content.content_id);
println!(" Content Type: WIP"); println!(" Content Type: {}", content.content_type);
println!(" Content Size: {} bytes", content.content_size); println!(" Content Size: {} bytes", content.content_size);
println!(" Content Hash: {}", hex::encode(content.content_hash)); println!(" Content Hash: {}", hex::encode(content.content_hash));
} }
@ -62,7 +62,7 @@ fn print_tmd_info(tmd: tmd::TMD) {
fn print_ticket_info(ticket: ticket::Ticket) { fn print_ticket_info(ticket: ticket::Ticket) {
// Print all important keys from the Ticket. // Print all important keys from the Ticket.
println!("Ticket Info"); println!("Ticket Info");
println!(" Title ID: {}", hex::encode(ticket.title_id)); println!(" Title ID: {}", hex::encode(ticket.title_id).to_uppercase());
println!(" Title Version: {}", ticket.title_version); println!(" Title Version: {}", ticket.title_version);
println!(" Ticket Version: {}", ticket.ticket_version); println!(" Ticket Version: {}", ticket.ticket_version);
let signature_issuer = String::from_utf8(Vec::from(ticket.signature_issuer)).unwrap_or_default(); let signature_issuer = String::from_utf8(Vec::from(ticket.signature_issuer)).unwrap_or_default();

View File

@ -6,12 +6,14 @@
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::io::{Cursor, Read, Write}; use std::io::{Cursor, Read, Write};
use std::ops::Index;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use sha1::{Sha1, Digest}; use sha1::{Sha1, Digest};
#[derive(Debug)] #[derive(Debug)]
pub enum TMDError { pub enum TMDError {
CannotFakesign, CannotFakesign,
InvalidContentType(u16),
IOError(std::io::Error), IOError(std::io::Error),
} }
@ -19,6 +21,7 @@ impl fmt::Display for TMDError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self { let description = match *self {
TMDError::CannotFakesign => "The TMD data could not be fakesigned.", TMDError::CannotFakesign => "The TMD data could not be fakesigned.",
TMDError::InvalidContentType(_) => "The TMD contains content with an invalid type.",
TMDError::IOError(_) => "The provided TMD data was invalid.", TMDError::IOError(_) => "The provided TMD data was invalid.",
}; };
f.write_str(description) f.write_str(description)
@ -27,12 +30,63 @@ impl fmt::Display for TMDError {
impl Error for TMDError {} impl Error for TMDError {}
#[derive(Debug)] pub enum TitleType {
#[derive(Clone)] System,
Game,
Channel,
SystemChannel,
GameChannel,
DLC,
HiddenChannel,
Unknown,
}
impl fmt::Display for TitleType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TitleType::System => write!(f, "System"),
TitleType::Game => write!(f, "Game"),
TitleType::Channel => write!(f, "Channel"),
TitleType::SystemChannel => write!(f, "SystemChannel"),
TitleType::GameChannel => write!(f, "GameChannel"),
TitleType::DLC => write!(f, "DLC"),
TitleType::HiddenChannel => write!(f, "HiddenChannel"),
TitleType::Unknown => write!(f, "Unknown"),
}
}
}
#[derive(Debug, Clone)]
pub enum ContentType {
Normal,
Development,
HashTree,
DLC,
Shared,
}
impl fmt::Display for ContentType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ContentType::Normal => write!(f, "Normal"),
ContentType::Development => write!(f, "Development/Unknown"),
ContentType::HashTree => write!(f, "Hash Tree"),
ContentType::DLC => write!(f, "DLC"),
ContentType::Shared => write!(f, "Shared"),
}
}
}
pub enum AccessRight {
AHB,
DVDVideo,
}
#[derive(Debug, Clone)]
pub struct ContentRecord { pub struct ContentRecord {
pub content_id: u32, pub content_id: u32,
pub index: u16, pub index: u16,
pub content_type: u16, pub content_type: ContentType,
pub content_size: u64, pub content_size: u64,
pub content_hash: [u8; 20], pub content_hash: [u8; 20],
} }
@ -52,7 +106,7 @@ pub struct TMD {
pub title_type: [u8; 4], pub title_type: [u8; 4],
pub group_id: u16, pub group_id: u16,
padding2: [u8; 2], padding2: [u8; 2],
pub region: u16, region: u16,
pub ratings: [u8; 16], pub ratings: [u8; 16],
reserved1: [u8; 12], reserved1: [u8; 12],
pub ipc_mask: [u8; 12], pub ipc_mask: [u8; 12],
@ -112,7 +166,15 @@ impl TMD {
for _ in 0..num_contents { for _ in 0..num_contents {
let content_id = buf.read_u32::<BigEndian>().map_err(TMDError::IOError)?; let content_id = buf.read_u32::<BigEndian>().map_err(TMDError::IOError)?;
let index = buf.read_u16::<BigEndian>().map_err(TMDError::IOError)?; let index = buf.read_u16::<BigEndian>().map_err(TMDError::IOError)?;
let content_type = buf.read_u16::<BigEndian>().map_err(TMDError::IOError)?; let type_int = buf.read_u16::<BigEndian>().map_err(TMDError::IOError)?;
let content_type = match type_int {
1 => ContentType::Normal,
2 => ContentType::Development,
3 => ContentType::HashTree,
16385 => ContentType::DLC,
32769 => ContentType::Shared,
_ => return Err(TMDError::InvalidContentType(type_int))
};
let content_size = buf.read_u64::<BigEndian>().map_err(TMDError::IOError)?; let content_size = buf.read_u64::<BigEndian>().map_err(TMDError::IOError)?;
let mut content_hash = [0u8; 20]; let mut content_hash = [0u8; 20];
buf.read_exact(&mut content_hash).map_err(TMDError::IOError)?; buf.read_exact(&mut content_hash).map_err(TMDError::IOError)?;
@ -182,7 +244,13 @@ impl TMD {
for content in &self.content_records { for content in &self.content_records {
buf.write_u32::<BigEndian>(content.content_id)?; buf.write_u32::<BigEndian>(content.content_id)?;
buf.write_u16::<BigEndian>(content.index)?; buf.write_u16::<BigEndian>(content.index)?;
buf.write_u16::<BigEndian>(content.content_type)?; match content.content_type {
ContentType::Normal => { buf.write_u16::<BigEndian>(1)?; },
ContentType::Development => { buf.write_u16::<BigEndian>(2)?; },
ContentType::HashTree => { buf.write_u16::<BigEndian>(3)?; },
ContentType::DLC => { buf.write_u16::<BigEndian>(16385)?; },
ContentType::Shared => { buf.write_u16::<BigEndian>(32769)?; }
}
buf.write_u64::<BigEndian>(content.content_size)?; buf.write_u64::<BigEndian>(content.content_size)?;
buf.write_all(&content.content_hash)?; buf.write_all(&content.content_hash)?;
} }
@ -221,4 +289,52 @@ impl TMD {
} }
Ok(()) Ok(())
} }
pub fn region(&self) -> &str {
match self.region {
0 => "JPN",
1 => "USA",
2 => "EUR",
3 => "None",
4 => "KOR",
_ => "Unknown",
}
}
pub fn title_type(&self) -> TitleType {
match hex::encode(self.title_id)[..8].to_string().as_str() {
"00000001" => TitleType::System,
"00010000" => TitleType::Game,
"00010001" => TitleType::Channel,
"00010002" => TitleType::SystemChannel,
"00010004" => TitleType::GameChannel,
"00010005" => TitleType::DLC,
"00010008" => TitleType::HiddenChannel,
_ => TitleType::Unknown,
}
}
pub fn content_type(&self, index: usize) -> ContentType {
// Find possible content indices, because the provided one could exist while the indices
// are out of order, which could cause problems finding the content.
let mut content_indices = Vec::new();
for record in &self.content_records {
content_indices.push(record.index);
}
let target_index = content_indices.index(index);
match self.content_records[*target_index as usize].content_type {
ContentType::Normal => ContentType::Normal,
ContentType::Development => ContentType::Development,
ContentType::HashTree => ContentType::HashTree,
ContentType::DLC => ContentType::DLC,
ContentType::Shared => ContentType::Shared,
}
}
pub fn check_access_right(&self, right: AccessRight) -> bool {
match right {
AccessRight::AHB => (self.access_rights & (1 << 0)) != 0,
AccessRight::DVDVideo => (self.access_rights & (1 << 1)) != 0,
}
}
} }