diff --git a/Cargo.lock b/Cargo.lock index d49221e..3f40e7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -441,7 +441,7 @@ dependencies = [ [[package]] name = "nod" -version = "1.4.2" +version = "1.4.3" dependencies = [ "adler", "aes", @@ -464,7 +464,7 @@ dependencies = [ [[package]] name = "nodtool" -version = "1.4.2" +version = "1.4.3" dependencies = [ "argp", "base16ct", diff --git a/Cargo.toml b/Cargo.toml index c1b25fe..3c69083 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ strip = "debuginfo" codegen-units = 1 [workspace.package] -version = "1.4.2" +version = "1.4.3" edition = "2021" rust-version = "1.74" authors = ["Luke Street "] diff --git a/nod/src/disc/gcn.rs b/nod/src/disc/gcn.rs index 7d5e9af..3d39b6c 100644 --- a/nod/src/disc/gcn.rs +++ b/nod/src/disc/gcn.rs @@ -214,7 +214,6 @@ pub(crate) fn read_part_meta( raw_apploader: raw_apploader.into_boxed_slice(), raw_fst, raw_dol: raw_dol.into_boxed_slice(), - raw_region: None, raw_ticket: None, raw_tmd: None, raw_cert_chain: None, diff --git a/nod/src/disc/mod.rs b/nod/src/disc/mod.rs index 72c51af..5438c20 100644 --- a/nod/src/disc/mod.rs +++ b/nod/src/disc/mod.rs @@ -365,8 +365,6 @@ pub struct PartitionMeta { pub raw_fst: Box<[u8]>, /// Main binary (main.dol) pub raw_dol: Box<[u8]>, - /// Disc region info (region.bin, Wii only) - pub raw_region: Option>, /// Ticket (ticket.bin, Wii only) pub raw_ticket: Option>, /// TMD (tmd.bin, Wii only) diff --git a/nod/src/disc/reader.rs b/nod/src/disc/reader.rs index 1cf154e..14cab0c 100644 --- a/nod/src/disc/reader.rs +++ b/nod/src/disc/reader.rs @@ -10,9 +10,10 @@ use super::{ hashes::{rebuild_hashes, HashTable}, wii::{PartitionWii, WiiPartEntry, WiiPartGroup, WiiPartitionHeader, WII_PART_GROUP_OFF}, DiscHeader, PartitionBase, PartitionHeader, PartitionKind, DL_DVD_SIZE, MINI_DVD_SIZE, - SL_DVD_SIZE, + REGION_SIZE, SL_DVD_SIZE, }; use crate::{ + disc::wii::REGION_OFFSET, io::block::{Block, BlockIO, PartitionInfo}, util::read::{read_box, read_from, read_vec}, DiscMeta, Error, OpenOptions, Result, ResultContext, SECTOR_SIZE, @@ -36,6 +37,7 @@ pub struct DiscReader { disc_header: Box, pub(crate) partitions: Vec, hash_tables: Vec, + region: Option<[u8; REGION_SIZE]>, } impl Clone for DiscReader { @@ -52,6 +54,7 @@ impl Clone for DiscReader { disc_header: self.disc_header.clone(), partitions: self.partitions.clone(), hash_tables: self.hash_tables.clone(), + region: self.region, } } } @@ -76,10 +79,13 @@ impl DiscReader { disc_header: DiscHeader::new_box_zeroed()?, partitions: vec![], hash_tables: vec![], + region: None, }; let disc_header: Box = read_box(&mut reader).context("Reading disc header")?; reader.disc_header = disc_header; if reader.disc_header.is_wii() { + reader.seek(SeekFrom::Start(REGION_OFFSET)).context("Seeking to region info")?; + reader.region = Some(read_from(&mut reader).context("Reading region info")?); reader.partitions = read_partition_info(&mut reader)?; // Rebuild hashes if the format requires it if (options.rebuild_encryption || options.validate_hashes) && meta.needs_hash_recovery { @@ -103,10 +109,16 @@ impl DiscReader { self.io.meta().disc_size.unwrap_or_else(|| guess_disc_size(&self.partitions)) } + #[inline] pub fn header(&self) -> &DiscHeader { &self.disc_header } + #[inline] + pub fn region(&self) -> Option<&[u8; REGION_SIZE]> { self.region.as_ref() } + + #[inline] pub fn partitions(&self) -> &[PartitionInfo] { &self.partitions } + #[inline] pub fn meta(&self) -> DiscMeta { self.io.meta() } /// Opens a new, decrypted partition read stream for the specified partition index. diff --git a/nod/src/disc/wii.rs b/nod/src/disc/wii.rs index 60c1f09..6541d29 100644 --- a/nod/src/disc/wii.rs +++ b/nod/src/disc/wii.rs @@ -21,10 +21,7 @@ use crate::{ KeyBytes, }, static_assert, - util::{ - div_rem, - read::{read_box, read_box_slice}, - }, + util::{div_rem, read::read_box_slice}, Error, OpenOptions, Result, ResultContext, }; @@ -279,7 +276,6 @@ pub struct PartitionWii { sector: u32, pos: u64, verify: bool, - raw_region: Box<[u8; REGION_SIZE]>, raw_tmd: Box<[u8]>, raw_cert_chain: Box<[u8]>, raw_h3_table: Box<[u8]>, @@ -297,7 +293,6 @@ impl Clone for PartitionWii { sector: u32::MAX, pos: 0, verify: self.verify, - raw_region: self.raw_region.clone(), raw_tmd: self.raw_tmd.clone(), raw_cert_chain: self.raw_cert_chain.clone(), raw_h3_table: self.raw_h3_table.clone(), @@ -315,10 +310,6 @@ impl PartitionWii { let block_size = inner.block_size(); let mut reader = PartitionGC::new(inner, disc_header)?; - // Read region - reader.seek(SeekFrom::Start(REGION_OFFSET)).context("Seeking to region offset")?; - let raw_region: Box<[u8; REGION_SIZE]> = read_box(&mut reader).context("Reading region")?; - // Read TMD, cert chain, and H3 table let offset = partition.start_sector as u64 * SECTOR_SIZE as u64; reader @@ -348,7 +339,6 @@ impl PartitionWii { sector: u32::MAX, pos: 0, verify: options.validate_hashes, - raw_region, raw_tmd, raw_cert_chain, raw_h3_table, @@ -498,7 +488,6 @@ impl PartitionBase for PartitionWii { fn meta(&mut self) -> Result> { self.seek(SeekFrom::Start(0)).context("Seeking to partition header")?; let mut meta = read_part_meta(self, true)?; - meta.raw_region = Some(self.raw_region.clone()); meta.raw_ticket = Some(Box::from(self.partition.header.ticket.as_bytes())); meta.raw_tmd = Some(self.raw_tmd.clone()); meta.raw_cert_chain = Some(self.raw_cert_chain.clone()); diff --git a/nod/src/lib.rs b/nod/src/lib.rs index 5a9f6d4..ed280f5 100644 --- a/nod/src/lib.rs +++ b/nod/src/lib.rs @@ -66,7 +66,8 @@ use std::{ pub use disc::{ ApploaderHeader, DiscHeader, DolHeader, FileStream, Fst, Node, NodeKind, OwnedFileStream, PartitionBase, PartitionHeader, PartitionKind, PartitionMeta, SignedHeader, Ticket, - TicketLimit, TmdHeader, WindowedStream, BI2_SIZE, BOOT_SIZE, GCN_MAGIC, SECTOR_SIZE, WII_MAGIC, + TicketLimit, TmdHeader, WindowedStream, BI2_SIZE, BOOT_SIZE, GCN_MAGIC, REGION_SIZE, + SECTOR_SIZE, WII_MAGIC, }; pub use io::{ block::{DiscStream, PartitionInfo}, @@ -209,6 +210,12 @@ impl Disc { #[inline] pub fn header(&self) -> &DiscHeader { self.reader.header() } + /// The Wii disc's region information. + /// + /// **GameCube**: This will return `None`. + #[inline] + pub fn region(&self) -> Option<&[u8; REGION_SIZE]> { self.reader.region() } + /// Returns extra metadata included in the disc file format, if any. #[inline] pub fn meta(&self) -> DiscMeta { self.reader.meta() } diff --git a/nodtool/src/cmd/extract.rs b/nodtool/src/cmd/extract.rs index bd1fb18..fdeb131 100644 --- a/nodtool/src/cmd/extract.rs +++ b/nodtool/src/cmd/extract.rs @@ -9,8 +9,7 @@ use std::{ use argp::FromArgs; use itertools::Itertools; use nod::{ - Disc, DiscHeader, Fst, Node, OpenOptions, PartitionBase, PartitionKind, PartitionMeta, - ResultContext, + Disc, Fst, Node, OpenOptions, PartitionBase, PartitionKind, PartitionMeta, ResultContext, }; use size::{Base, Size}; use zerocopy::IntoBytes; @@ -65,38 +64,38 @@ pub fn run(args: Args) -> nod::Result<()> { let mut out_dir = output_dir.clone(); out_dir.push(info.kind.dir_name().as_ref()); let mut partition = disc.open_partition(info.index)?; - extract_partition(header, partition.as_mut(), &out_dir, is_wii, args.quiet)?; + 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)?; - extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?; + 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)?; - extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?; + 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)?; - extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?; + extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?; } else { let idx = partition.parse::().map_err(|_| "Invalid partition index")?; let mut partition = disc.open_partition(idx)?; - extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?; + extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?; } } else { let mut partition = disc.open_partition_kind(PartitionKind::Data)?; - extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?; + extract_partition(&disc, partition.as_mut(), &output_dir, is_wii, args.quiet)?; } Ok(()) } fn extract_partition( - header: &DiscHeader, + disc: &Disc, partition: &mut dyn PartitionBase, out_dir: &Path, is_wii: bool, quiet: bool, ) -> nod::Result<()> { let meta = partition.meta()?; - extract_sys_files(header, meta.as_ref(), out_dir, quiet)?; + extract_sys_files(disc, meta.as_ref(), out_dir, quiet)?; // Extract FST let files_dir = out_dir.join("files"); @@ -132,7 +131,7 @@ fn extract_partition( } fn extract_sys_files( - header: &DiscHeader, + disc: &Disc, data: &PartitionMeta, out_dir: &Path, quiet: bool, @@ -147,12 +146,13 @@ fn extract_sys_files( extract_file(data.raw_dol.as_ref(), &sys_dir.join("main.dol"), quiet)?; // Wii files - if header.is_wii() { + let disc_header = disc.header(); + if disc_header.is_wii() { let disc_dir = out_dir.join("disc"); fs::create_dir_all(&disc_dir) .with_context(|| format!("Creating directory {}", display(&disc_dir)))?; - extract_file(&header.as_bytes()[..0x100], &disc_dir.join("header.bin"), quiet)?; - if let Some(region) = data.raw_region.as_deref() { + extract_file(&disc_header.as_bytes()[..0x100], &disc_dir.join("header.bin"), quiet)?; + if let Some(region) = disc.region() { extract_file(region, &disc_dir.join("region.bin"), quiet)?; } if let Some(ticket) = data.raw_ticket.as_deref() {