mirror of
https://github.com/NinjaCheetah/rustii.git
synced 2025-06-06 15:31:02 -04:00
Filled in many info command blanks
This commit is contained in:
parent
8c7cd48dff
commit
3178063a07
@ -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();
|
||||||
|
128
src/title/tmd.rs
128
src/title/tmd.rs
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user