mirror of
https://github.com/NinjaCheetah/rustii.git
synced 2026-03-04 11:55:27 -05:00
Ported wad convert command from WiiPy
This commit is contained in:
@@ -92,7 +92,13 @@ fn print_tmd_info(tmd: tmd::TMD, cert: Option<cert::Certificate>) {
|
||||
}
|
||||
},
|
||||
},
|
||||
Err(_) => "Invalid (Modified TMD)"
|
||||
Err(_) => {
|
||||
if tmd.is_fakesigned() {
|
||||
"Fakesigned"
|
||||
} else {
|
||||
"Invalid (Modified TMD)"
|
||||
}
|
||||
}
|
||||
};
|
||||
println!(" Signature: {}", signing_str);
|
||||
} else {
|
||||
@@ -120,12 +126,11 @@ fn print_ticket_info(ticket: ticket::Ticket, cert: Option<cert::Certificate>) {
|
||||
} else {
|
||||
println!(" Title ID: {}", hex::encode(ticket.title_id).to_uppercase());
|
||||
}
|
||||
if hex::encode(ticket.title_id)[..8].eq("00000001") {
|
||||
if hex::encode(ticket.title_id).eq("0000000100000001") {
|
||||
println!(" Title Version: {} (boot2v{})", ticket.title_version, ticket.title_version);
|
||||
} else {
|
||||
println!(" Title Version: {} ({})", ticket.title_version, versions::dec_to_standard(ticket.title_version, &hex::encode(ticket.title_id), Some(ticket.common_key_index == 2)).unwrap());
|
||||
}
|
||||
let converted_ver = versions::dec_to_standard(ticket.title_version, &hex::encode(ticket.title_id), None);
|
||||
if hex::encode(ticket.title_id).eq("0000000100000001") {
|
||||
println!(" Title Version: {} (boot2v{})", ticket.title_version, ticket.title_version);
|
||||
} else if hex::encode(ticket.title_id)[..8].eq("00000001") && converted_ver.is_some() {
|
||||
println!(" Title Version: {} ({})", ticket.title_version, converted_ver.unwrap());
|
||||
} else {
|
||||
println!(" Title Version: {}", ticket.title_version);
|
||||
}
|
||||
@@ -167,7 +172,13 @@ fn print_ticket_info(ticket: ticket::Ticket, cert: Option<cert::Certificate>) {
|
||||
}
|
||||
},
|
||||
},
|
||||
Err(_) => "Invalid (Modified Ticket)"
|
||||
Err(_) => {
|
||||
if ticket.is_fakesigned() {
|
||||
"Fakesigned"
|
||||
} else {
|
||||
"Invalid (Modified Ticket)"
|
||||
}
|
||||
}
|
||||
};
|
||||
println!(" Signature: {}", signing_str);
|
||||
} else {
|
||||
@@ -214,7 +225,13 @@ fn print_wad_info(wad: wad::WAD) {
|
||||
}
|
||||
},
|
||||
},
|
||||
Err(_) => "Illegitimate (Modified TMD + Ticket)"
|
||||
Err(_) => {
|
||||
if title.is_fakesigned() {
|
||||
"Fakesigned"
|
||||
} else {
|
||||
"Illegitimate (Modified TMD + Ticket)"
|
||||
}
|
||||
}
|
||||
};
|
||||
println!(" Signing Status: {}", signing_str);
|
||||
println!();
|
||||
|
||||
@@ -7,6 +7,7 @@ mod title;
|
||||
mod filetypes;
|
||||
mod info;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Subcommand, Parser};
|
||||
use title::{wad, fakesign};
|
||||
|
||||
@@ -40,14 +41,14 @@ enum Commands {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
match &cli.command {
|
||||
Some(Commands::Wad { command }) => {
|
||||
match command {
|
||||
Some(wad::Commands::Convert { input, output }) => {
|
||||
wad::convert_wad(input, output)
|
||||
Some(wad::Commands::Convert { input, target, output }) => {
|
||||
wad::convert_wad(input, target, output)?
|
||||
},
|
||||
Some(wad::Commands::Pack { input, output}) => {
|
||||
wad::pack_wad(input, output)
|
||||
@@ -66,4 +67,5 @@ fn main() {
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
//
|
||||
// Code for WAD-related commands in the rustii CLI.
|
||||
|
||||
use std::{str, fs};
|
||||
use std::{str, fs, fmt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::{Subcommand, Args};
|
||||
use glob::glob;
|
||||
use rustii::title::{cert, tmd, ticket, content, wad};
|
||||
use rustii::title::{cert, crypto, tmd, ticket, content, wad};
|
||||
use rustii::title;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
@@ -20,6 +21,8 @@ pub enum Commands {
|
||||
/// An (optional) WAD name; defaults to <input name>_<new type>.wad
|
||||
#[arg(short, long)]
|
||||
output: Option<String>,
|
||||
#[command(flatten)]
|
||||
target: ConvertTargets,
|
||||
},
|
||||
/// Pack a directory into a WAD file
|
||||
Pack {
|
||||
@@ -38,13 +41,103 @@ pub enum Commands {
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[clap(next_help_heading = "Encryption Targets")]
|
||||
#[group(multiple = false, required = true)]
|
||||
struct ConvertTargets {
|
||||
|
||||
pub struct ConvertTargets {
|
||||
/// Use the retail common key, allowing this WAD to be installed on retail consoles and Dolphin
|
||||
#[arg(long)]
|
||||
retail: bool,
|
||||
/// Use the development common key, allowing this WAD to be installed on development consoles
|
||||
#[arg(long)]
|
||||
dev: bool,
|
||||
/// Use the vWii key, allowing this WAD to theoretically be installed from Wii U mode if a Wii U mode WAD installer is created
|
||||
#[arg(long)]
|
||||
vwii: bool,
|
||||
}
|
||||
|
||||
pub fn convert_wad(input: &str, output: &Option<String>) {
|
||||
todo!();
|
||||
enum Target {
|
||||
Retail,
|
||||
Dev,
|
||||
Vwii,
|
||||
}
|
||||
|
||||
impl fmt::Display for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Target::Retail => write!(f, "retail"),
|
||||
Target::Dev => write!(f, "development"),
|
||||
Target::Vwii => write!(f, "vWii"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_wad(input: &str, target: &ConvertTargets, output: &Option<String>) -> Result<()> {
|
||||
let in_path = Path::new(input);
|
||||
if !in_path.exists() {
|
||||
bail!("Source WAD \"{}\" could not be found.", input);
|
||||
}
|
||||
// Parse the target passed to identify the encryption target.
|
||||
let target = if target.dev {
|
||||
Target::Dev
|
||||
} else if target.vwii {
|
||||
Target::Vwii
|
||||
} else {
|
||||
Target::Retail
|
||||
};
|
||||
// Get the output name now that we know the target, if one wasn't passed.
|
||||
let out_path = if output.is_some() {
|
||||
PathBuf::from(output.clone().unwrap()).with_extension("wad")
|
||||
} else {
|
||||
match target {
|
||||
Target::Retail => PathBuf::from(format!("{}_retail", in_path.file_stem().unwrap().to_str().unwrap())).with_extension("wad"),
|
||||
Target::Dev => PathBuf::from(format!("{}_dev", in_path.file_stem().unwrap().to_str().unwrap())).with_extension("wad"),
|
||||
Target::Vwii => PathBuf::from(format!("{}_vWii", in_path.file_stem().unwrap().to_str().unwrap())).with_extension("wad"),
|
||||
}
|
||||
};
|
||||
let mut title = title::Title::from_bytes(fs::read(in_path)?.as_slice()).with_context(|| "The provided WAD file could not be loaded, and is likely invalid.")?;
|
||||
// Bail if the WAD is already using the selected encryption.
|
||||
if matches!(target, Target::Dev) && title.ticket.is_dev() {
|
||||
bail!("This is already a development WAD!");
|
||||
} else if matches!(target, Target::Retail) && !title.ticket.is_dev() && !title.tmd.is_vwii() {
|
||||
bail!("This is already a retail WAD!");
|
||||
} else if matches!(target, Target::Vwii) && !title.ticket.is_dev() && title.tmd.is_vwii() {
|
||||
bail!("This is already a vWii WAD!");
|
||||
}
|
||||
// Save the current encryption to display at the end.
|
||||
let source = if title.ticket.is_dev() {
|
||||
"development"
|
||||
} else if title.tmd.is_vwii() {
|
||||
"vWii"
|
||||
} else {
|
||||
"retail"
|
||||
};
|
||||
let title_key = title.ticket.dec_title_key();
|
||||
let title_key_new: [u8; 16];
|
||||
match target {
|
||||
Target::Dev => {
|
||||
title.tmd.set_signature_issuer(String::from("Root-CA00000002-CP00000007"))?;
|
||||
title.ticket.set_signature_issuer(String::from("Root-CA00000002-XS00000006"))?;
|
||||
title_key_new = crypto::encrypt_title_key(title_key, 0, title.ticket.title_id, Some(true));
|
||||
title.ticket.common_key_index = 0;
|
||||
},
|
||||
Target::Retail => {
|
||||
title.tmd.set_signature_issuer(String::from("Root-CA00000001-CP00000004"))?;
|
||||
title.ticket.set_signature_issuer(String::from("Root-CA00000001-XS00000003"))?;
|
||||
title_key_new = crypto::encrypt_title_key(title_key, 0, title.ticket.title_id, Some(false));
|
||||
title.ticket.common_key_index = 0;
|
||||
},
|
||||
Target::Vwii => {
|
||||
title.tmd.set_signature_issuer(String::from("Root-CA00000001-CP00000004"))?;
|
||||
title.ticket.set_signature_issuer(String::from("Root-CA00000001-XS00000003"))?;
|
||||
title_key_new = crypto::encrypt_title_key(title_key, 2, title.ticket.title_id, Some(false));
|
||||
title.ticket.common_key_index = 2;
|
||||
}
|
||||
}
|
||||
title.ticket.title_key = title_key_new;
|
||||
title.fakesign()?;
|
||||
fs::write(out_path.clone(), title.to_wad()?.to_bytes()?)?;
|
||||
println!("Successfully converted {} WAD to {} WAD \"{}\"!", source, target, out_path.file_name().unwrap().to_str().unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pack_wad(input: &str, output: &str) {
|
||||
|
||||
Reference in New Issue
Block a user