Support decrypted discs & decrypt/encrypt conversion

This commit is contained in:
2024-11-07 23:56:43 -07:00
parent df8ab228c8
commit 374c6950b2
23 changed files with 453 additions and 222 deletions

View File

@@ -30,7 +30,7 @@ indicatif = "0.17"
itertools = "0.13"
log = "0.4"
md-5 = "0.10"
nod = { version = "1.2", path = "../nod" }
nod = { version = "2.0.0-alpha", path = "../nod" }
quick-xml = { version = "0.36", features = ["serialize"] }
serde = { version = "1.0", features = ["derive"] }
sha1 = "0.10"

View File

@@ -1,6 +1,7 @@
use std::path::PathBuf;
use argp::FromArgs;
use nod::OpenOptions;
use crate::util::{redump, shared::convert_and_verify};
@@ -20,6 +21,12 @@ pub struct Args {
#[argp(option, short = 'd')]
/// path to DAT file(s) for verification (optional)
dat: Vec<PathBuf>,
#[argp(switch)]
/// decrypt Wii partition data
decrypt: bool,
#[argp(switch)]
/// encrypt Wii partition data
encrypt: bool,
}
pub fn run(args: Args) -> nod::Result<()> {
@@ -27,5 +34,15 @@ pub fn run(args: Args) -> nod::Result<()> {
println!("Loading dat files...");
redump::load_dats(args.dat.iter().map(PathBuf::as_ref))?;
}
convert_and_verify(&args.file, Some(&args.out), args.md5)
let options = OpenOptions {
partition_encryption: match (args.decrypt, args.encrypt) {
(true, false) => nod::PartitionEncryptionMode::ForceDecrypted,
(false, true) => nod::PartitionEncryptionMode::ForceEncrypted,
(false, false) => nod::PartitionEncryptionMode::Original,
(true, true) => {
return Err(nod::Error::Other("Both --decrypt and --encrypt specified".to_string()))
}
},
};
convert_and_verify(&args.file, Some(&args.out), args.md5, &options)
}

View File

@@ -10,7 +10,7 @@ use std::{
use argp::FromArgs;
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
use nod::{Disc, OpenOptions, Result, ResultContext};
use nod::{Disc, OpenOptions, PartitionEncryptionMode, Result, ResultContext};
use zerocopy::FromZeros;
use crate::util::{
@@ -165,10 +165,8 @@ struct DiscHashes {
}
fn load_disc(path: &Path, name: &str, full_verify: bool) -> Result<DiscHashes> {
let mut disc = Disc::new_with_options(path, &OpenOptions {
rebuild_encryption: true,
validate_hashes: false,
})?;
let options = OpenOptions { partition_encryption: PartitionEncryptionMode::Original };
let mut disc = Disc::new_with_options(path, &options)?;
let disc_size = disc.disc_size();
if !full_verify {
let meta = disc.meta();

View File

@@ -9,7 +9,8 @@ use std::{
use argp::FromArgs;
use itertools::Itertools;
use nod::{
Disc, Fst, Node, OpenOptions, PartitionBase, PartitionKind, PartitionMeta, ResultContext,
Disc, Fst, Node, OpenOptions, PartitionBase, PartitionKind, PartitionMeta, PartitionOptions,
ResultContext,
};
use size::{Base, Size};
use zerocopy::IntoBytes;
@@ -52,36 +53,39 @@ pub fn run(args: Args) -> nod::Result<()> {
} else {
output_dir = args.file.with_extension("");
}
let disc = Disc::new_with_options(&args.file, &OpenOptions {
rebuild_encryption: false,
validate_hashes: args.validate,
})?;
let disc = Disc::new_with_options(&args.file, &OpenOptions::default())?;
let header = disc.header();
let is_wii = header.is_wii();
let partition_options = PartitionOptions { validate_hashes: args.validate };
if let Some(partition) = args.partition {
if partition.eq_ignore_ascii_case("all") {
for info in disc.partitions() {
let mut out_dir = output_dir.clone();
out_dir.push(info.kind.dir_name().as_ref());
let mut partition = disc.open_partition(info.index)?;
let mut partition =
disc.open_partition_with_options(info.index, &partition_options)?;
extract_partition(&disc, partition.as_mut(), &out_dir, is_wii, args.quiet)?;
}
} else if partition.eq_ignore_ascii_case("data") {
let mut partition = disc.open_partition_kind(PartitionKind::Data)?;
let mut partition =
disc.open_partition_kind_with_options(PartitionKind::Data, &partition_options)?;
extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
} else if partition.eq_ignore_ascii_case("update") {
let mut partition = disc.open_partition_kind(PartitionKind::Update)?;
let mut partition =
disc.open_partition_kind_with_options(PartitionKind::Update, &partition_options)?;
extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
} else if partition.eq_ignore_ascii_case("channel") {
let mut partition = disc.open_partition_kind(PartitionKind::Channel)?;
let mut partition =
disc.open_partition_kind_with_options(PartitionKind::Channel, &partition_options)?;
extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
} else {
let idx = partition.parse::<usize>().map_err(|_| "Invalid partition index")?;
let mut partition = disc.open_partition(idx)?;
let mut partition = disc.open_partition_with_options(idx, &partition_options)?;
extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
}
} else {
let mut partition = disc.open_partition_kind(PartitionKind::Data)?;
let mut partition =
disc.open_partition_kind_with_options(PartitionKind::Data, &partition_options)?;
extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
}
Ok(())

View File

@@ -1,7 +1,7 @@
use std::path::{Path, PathBuf};
use argp::FromArgs;
use nod::{Disc, OpenOptions, SECTOR_SIZE};
use nod::{Disc, SECTOR_SIZE};
use size::Size;
use crate::util::{display, shared::print_header};
@@ -24,16 +24,16 @@ pub fn run(args: Args) -> nod::Result<()> {
fn info_file(path: &Path) -> nod::Result<()> {
log::info!("Loading {}", display(path));
let disc = Disc::new_with_options(path, &OpenOptions {
rebuild_encryption: false,
validate_hashes: false,
})?;
let disc = Disc::new(path)?;
let header = disc.header();
let meta = disc.meta();
print_header(header, &meta);
if header.is_wii() {
for (idx, info) in disc.partitions().iter().enumerate() {
let mut partition = disc.open_partition(idx)?;
let meta = partition.meta()?;
println!();
println!("Partition {}", idx);
println!("\tType: {}", info.kind);
@@ -41,43 +41,50 @@ fn info_file(path: &Path) -> nod::Result<()> {
println!("\tStart sector: {} (offset {:#X})", info.start_sector, offset);
let data_size =
(info.data_end_sector - info.data_start_sector) as u64 * SECTOR_SIZE as u64;
if info.has_encryption {
println!(
"\tEncrypted data offset / size: {:#X} / {:#X} ({})",
info.data_start_sector as u64 * SECTOR_SIZE as u64,
data_size,
Size::from_bytes(data_size)
);
} else {
println!(
"\tDecrypted data offset / size: {:#X} / {:#X} ({})",
offset,
data_size,
Size::from_bytes(data_size)
);
}
println!(
"\tData offset / size: {:#X} / {:#X} ({})",
info.data_start_sector as u64 * SECTOR_SIZE as u64,
data_size,
Size::from_bytes(data_size)
);
println!(
"\tTMD offset / size: {:#X} / {:#X}",
"\tTMD offset / size: {:#X} / {:#X}",
offset + info.header.tmd_off(),
info.header.tmd_size()
);
if let Some(content_metadata) = meta.content_metadata() {
for content in content_metadata {
println!(
"\t-> Content {:08X} size: {:#X} ({})",
content.content_index.get(),
content.size.get(),
Size::from_bytes(content.size.get()),
);
}
}
println!(
"\tCert offset / size: {:#X} / {:#X}",
"\tCert chain offset / size: {:#X} / {:#X}",
offset + info.header.cert_chain_off(),
info.header.cert_chain_size()
);
println!(
"\tH3 offset / size: {:#X} / {:#X}",
"\tH3 table offset / size: {:#X} / {:#X}",
offset + info.header.h3_table_off(),
info.header.h3_table_size()
);
let mut partition = disc.open_partition(idx)?;
let meta = partition.meta()?;
let tmd = meta.tmd_header();
let title_id_str = if let Some(tmd) = tmd {
format!(
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
tmd.title_id[0],
tmd.title_id[1],
tmd.title_id[2],
tmd.title_id[3],
tmd.title_id[4],
tmd.title_id[5],
tmd.title_id[6],
tmd.title_id[7]
)
hex::encode_upper(tmd.title_id)
} else {
"N/A".to_string()
};

View File

@@ -1,6 +1,7 @@
use std::path::PathBuf;
use argp::FromArgs;
use nod::{OpenOptions, PartitionEncryptionMode};
use crate::util::{redump, shared::convert_and_verify};
@@ -24,8 +25,9 @@ pub fn run(args: Args) -> nod::Result<()> {
println!("Loading dat files...");
redump::load_dats(args.dat.iter().map(PathBuf::as_ref))?;
}
let options = OpenOptions { partition_encryption: PartitionEncryptionMode::Original };
for file in &args.file {
convert_and_verify(file, None, args.md5)?;
convert_and_verify(file, None, args.md5, &options)?;
println!();
}
Ok(())

View File

@@ -38,20 +38,22 @@ pub fn print_header(header: &DiscHeader, meta: &DiscMeta) {
println!("Title: {}", header.game_title_str());
println!("Game ID: {}", header.game_id_str());
println!("Disc {}, Revision {}", header.disc_num + 1, header.disc_version);
if header.no_partition_hashes != 0 {
if !header.has_partition_hashes() {
println!("[!] Disc has no hashes");
}
if header.no_partition_encryption != 0 {
if !header.has_partition_encryption() {
println!("[!] Disc is not encrypted");
}
}
pub fn convert_and_verify(in_file: &Path, out_file: Option<&Path>, md5: bool) -> Result<()> {
pub fn convert_and_verify(
in_file: &Path,
out_file: Option<&Path>,
md5: bool,
options: &OpenOptions,
) -> Result<()> {
println!("Loading {}", display(in_file));
let mut disc = Disc::new_with_options(in_file, &OpenOptions {
rebuild_encryption: true,
validate_hashes: false,
})?;
let mut disc = Disc::new_with_options(in_file, options)?;
let header = disc.header();
let meta = disc.meta();
print_header(header, &meta);