Move region info from PartitionMeta to Disc

This commit is contained in:
Luke Street 2024-10-04 20:17:16 -06:00
parent f4638369d1
commit be4672471d
8 changed files with 39 additions and 34 deletions

4
Cargo.lock generated
View File

@ -441,7 +441,7 @@ dependencies = [
[[package]] [[package]]
name = "nod" name = "nod"
version = "1.4.2" version = "1.4.3"
dependencies = [ dependencies = [
"adler", "adler",
"aes", "aes",
@ -464,7 +464,7 @@ dependencies = [
[[package]] [[package]]
name = "nodtool" name = "nodtool"
version = "1.4.2" version = "1.4.3"
dependencies = [ dependencies = [
"argp", "argp",
"base16ct", "base16ct",

View File

@ -9,7 +9,7 @@ strip = "debuginfo"
codegen-units = 1 codegen-units = 1
[workspace.package] [workspace.package]
version = "1.4.2" version = "1.4.3"
edition = "2021" edition = "2021"
rust-version = "1.74" rust-version = "1.74"
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]

View File

@ -214,7 +214,6 @@ pub(crate) fn read_part_meta(
raw_apploader: raw_apploader.into_boxed_slice(), raw_apploader: raw_apploader.into_boxed_slice(),
raw_fst, raw_fst,
raw_dol: raw_dol.into_boxed_slice(), raw_dol: raw_dol.into_boxed_slice(),
raw_region: None,
raw_ticket: None, raw_ticket: None,
raw_tmd: None, raw_tmd: None,
raw_cert_chain: None, raw_cert_chain: None,

View File

@ -365,8 +365,6 @@ pub struct PartitionMeta {
pub raw_fst: Box<[u8]>, pub raw_fst: Box<[u8]>,
/// Main binary (main.dol) /// Main binary (main.dol)
pub raw_dol: Box<[u8]>, pub raw_dol: Box<[u8]>,
/// Disc region info (region.bin, Wii only)
pub raw_region: Option<Box<[u8; REGION_SIZE]>>,
/// Ticket (ticket.bin, Wii only) /// Ticket (ticket.bin, Wii only)
pub raw_ticket: Option<Box<[u8]>>, pub raw_ticket: Option<Box<[u8]>>,
/// TMD (tmd.bin, Wii only) /// TMD (tmd.bin, Wii only)

View File

@ -10,9 +10,10 @@ use super::{
hashes::{rebuild_hashes, HashTable}, hashes::{rebuild_hashes, HashTable},
wii::{PartitionWii, WiiPartEntry, WiiPartGroup, WiiPartitionHeader, WII_PART_GROUP_OFF}, wii::{PartitionWii, WiiPartEntry, WiiPartGroup, WiiPartitionHeader, WII_PART_GROUP_OFF},
DiscHeader, PartitionBase, PartitionHeader, PartitionKind, DL_DVD_SIZE, MINI_DVD_SIZE, DiscHeader, PartitionBase, PartitionHeader, PartitionKind, DL_DVD_SIZE, MINI_DVD_SIZE,
SL_DVD_SIZE, REGION_SIZE, SL_DVD_SIZE,
}; };
use crate::{ use crate::{
disc::wii::REGION_OFFSET,
io::block::{Block, BlockIO, PartitionInfo}, io::block::{Block, BlockIO, PartitionInfo},
util::read::{read_box, read_from, read_vec}, util::read::{read_box, read_from, read_vec},
DiscMeta, Error, OpenOptions, Result, ResultContext, SECTOR_SIZE, DiscMeta, Error, OpenOptions, Result, ResultContext, SECTOR_SIZE,
@ -36,6 +37,7 @@ pub struct DiscReader {
disc_header: Box<DiscHeader>, disc_header: Box<DiscHeader>,
pub(crate) partitions: Vec<PartitionInfo>, pub(crate) partitions: Vec<PartitionInfo>,
hash_tables: Vec<HashTable>, hash_tables: Vec<HashTable>,
region: Option<[u8; REGION_SIZE]>,
} }
impl Clone for DiscReader { impl Clone for DiscReader {
@ -52,6 +54,7 @@ impl Clone for DiscReader {
disc_header: self.disc_header.clone(), disc_header: self.disc_header.clone(),
partitions: self.partitions.clone(), partitions: self.partitions.clone(),
hash_tables: self.hash_tables.clone(), hash_tables: self.hash_tables.clone(),
region: self.region,
} }
} }
} }
@ -76,10 +79,13 @@ impl DiscReader {
disc_header: DiscHeader::new_box_zeroed()?, disc_header: DiscHeader::new_box_zeroed()?,
partitions: vec![], partitions: vec![],
hash_tables: vec![], hash_tables: vec![],
region: None,
}; };
let disc_header: Box<DiscHeader> = read_box(&mut reader).context("Reading disc header")?; let disc_header: Box<DiscHeader> = read_box(&mut reader).context("Reading disc header")?;
reader.disc_header = disc_header; reader.disc_header = disc_header;
if reader.disc_header.is_wii() { 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)?; reader.partitions = read_partition_info(&mut reader)?;
// Rebuild hashes if the format requires it // Rebuild hashes if the format requires it
if (options.rebuild_encryption || options.validate_hashes) && meta.needs_hash_recovery { 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)) self.io.meta().disc_size.unwrap_or_else(|| guess_disc_size(&self.partitions))
} }
#[inline]
pub fn header(&self) -> &DiscHeader { &self.disc_header } 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 } pub fn partitions(&self) -> &[PartitionInfo] { &self.partitions }
#[inline]
pub fn meta(&self) -> DiscMeta { self.io.meta() } pub fn meta(&self) -> DiscMeta { self.io.meta() }
/// Opens a new, decrypted partition read stream for the specified partition index. /// Opens a new, decrypted partition read stream for the specified partition index.

View File

@ -21,10 +21,7 @@ use crate::{
KeyBytes, KeyBytes,
}, },
static_assert, static_assert,
util::{ util::{div_rem, read::read_box_slice},
div_rem,
read::{read_box, read_box_slice},
},
Error, OpenOptions, Result, ResultContext, Error, OpenOptions, Result, ResultContext,
}; };
@ -279,7 +276,6 @@ pub struct PartitionWii {
sector: u32, sector: u32,
pos: u64, pos: u64,
verify: bool, verify: bool,
raw_region: Box<[u8; REGION_SIZE]>,
raw_tmd: Box<[u8]>, raw_tmd: Box<[u8]>,
raw_cert_chain: Box<[u8]>, raw_cert_chain: Box<[u8]>,
raw_h3_table: Box<[u8]>, raw_h3_table: Box<[u8]>,
@ -297,7 +293,6 @@ impl Clone for PartitionWii {
sector: u32::MAX, sector: u32::MAX,
pos: 0, pos: 0,
verify: self.verify, verify: self.verify,
raw_region: self.raw_region.clone(),
raw_tmd: self.raw_tmd.clone(), raw_tmd: self.raw_tmd.clone(),
raw_cert_chain: self.raw_cert_chain.clone(), raw_cert_chain: self.raw_cert_chain.clone(),
raw_h3_table: self.raw_h3_table.clone(), raw_h3_table: self.raw_h3_table.clone(),
@ -315,10 +310,6 @@ impl PartitionWii {
let block_size = inner.block_size(); let block_size = inner.block_size();
let mut reader = PartitionGC::new(inner, disc_header)?; 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 // Read TMD, cert chain, and H3 table
let offset = partition.start_sector as u64 * SECTOR_SIZE as u64; let offset = partition.start_sector as u64 * SECTOR_SIZE as u64;
reader reader
@ -348,7 +339,6 @@ impl PartitionWii {
sector: u32::MAX, sector: u32::MAX,
pos: 0, pos: 0,
verify: options.validate_hashes, verify: options.validate_hashes,
raw_region,
raw_tmd, raw_tmd,
raw_cert_chain, raw_cert_chain,
raw_h3_table, raw_h3_table,
@ -498,7 +488,6 @@ impl PartitionBase for PartitionWii {
fn meta(&mut self) -> Result<Box<PartitionMeta>> { fn meta(&mut self) -> Result<Box<PartitionMeta>> {
self.seek(SeekFrom::Start(0)).context("Seeking to partition header")?; self.seek(SeekFrom::Start(0)).context("Seeking to partition header")?;
let mut meta = read_part_meta(self, true)?; 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_ticket = Some(Box::from(self.partition.header.ticket.as_bytes()));
meta.raw_tmd = Some(self.raw_tmd.clone()); meta.raw_tmd = Some(self.raw_tmd.clone());
meta.raw_cert_chain = Some(self.raw_cert_chain.clone()); meta.raw_cert_chain = Some(self.raw_cert_chain.clone());

View File

@ -66,7 +66,8 @@ use std::{
pub use disc::{ pub use disc::{
ApploaderHeader, DiscHeader, DolHeader, FileStream, Fst, Node, NodeKind, OwnedFileStream, ApploaderHeader, DiscHeader, DolHeader, FileStream, Fst, Node, NodeKind, OwnedFileStream,
PartitionBase, PartitionHeader, PartitionKind, PartitionMeta, SignedHeader, Ticket, 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::{ pub use io::{
block::{DiscStream, PartitionInfo}, block::{DiscStream, PartitionInfo},
@ -209,6 +210,12 @@ impl Disc {
#[inline] #[inline]
pub fn header(&self) -> &DiscHeader { self.reader.header() } 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. /// Returns extra metadata included in the disc file format, if any.
#[inline] #[inline]
pub fn meta(&self) -> DiscMeta { self.reader.meta() } pub fn meta(&self) -> DiscMeta { self.reader.meta() }

View File

@ -9,8 +9,7 @@ use std::{
use argp::FromArgs; use argp::FromArgs;
use itertools::Itertools; use itertools::Itertools;
use nod::{ use nod::{
Disc, DiscHeader, Fst, Node, OpenOptions, PartitionBase, PartitionKind, PartitionMeta, Disc, Fst, Node, OpenOptions, PartitionBase, PartitionKind, PartitionMeta, ResultContext,
ResultContext,
}; };
use size::{Base, Size}; use size::{Base, Size};
use zerocopy::IntoBytes; use zerocopy::IntoBytes;
@ -65,38 +64,38 @@ pub fn run(args: Args) -> nod::Result<()> {
let mut out_dir = output_dir.clone(); let mut out_dir = output_dir.clone();
out_dir.push(info.kind.dir_name().as_ref()); out_dir.push(info.kind.dir_name().as_ref());
let mut partition = disc.open_partition(info.index)?; 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") { } else if partition.eq_ignore_ascii_case("data") {
let mut partition = disc.open_partition_kind(PartitionKind::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") { } else if partition.eq_ignore_ascii_case("update") {
let mut partition = disc.open_partition_kind(PartitionKind::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") { } else if partition.eq_ignore_ascii_case("channel") {
let mut partition = disc.open_partition_kind(PartitionKind::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 { } else {
let idx = partition.parse::<usize>().map_err(|_| "Invalid partition index")?; let idx = partition.parse::<usize>().map_err(|_| "Invalid partition index")?;
let mut partition = disc.open_partition(idx)?; 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 { } else {
let mut partition = disc.open_partition_kind(PartitionKind::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)?;
} }
Ok(()) Ok(())
} }
fn extract_partition( fn extract_partition(
header: &DiscHeader, disc: &Disc,
partition: &mut dyn PartitionBase, partition: &mut dyn PartitionBase,
out_dir: &Path, out_dir: &Path,
is_wii: bool, is_wii: bool,
quiet: bool, quiet: bool,
) -> nod::Result<()> { ) -> nod::Result<()> {
let meta = partition.meta()?; 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 // Extract FST
let files_dir = out_dir.join("files"); let files_dir = out_dir.join("files");
@ -132,7 +131,7 @@ fn extract_partition(
} }
fn extract_sys_files( fn extract_sys_files(
header: &DiscHeader, disc: &Disc,
data: &PartitionMeta, data: &PartitionMeta,
out_dir: &Path, out_dir: &Path,
quiet: bool, quiet: bool,
@ -147,12 +146,13 @@ fn extract_sys_files(
extract_file(data.raw_dol.as_ref(), &sys_dir.join("main.dol"), quiet)?; extract_file(data.raw_dol.as_ref(), &sys_dir.join("main.dol"), quiet)?;
// Wii files // Wii files
if header.is_wii() { let disc_header = disc.header();
if disc_header.is_wii() {
let disc_dir = out_dir.join("disc"); let disc_dir = out_dir.join("disc");
fs::create_dir_all(&disc_dir) fs::create_dir_all(&disc_dir)
.with_context(|| format!("Creating directory {}", display(&disc_dir)))?; .with_context(|| format!("Creating directory {}", display(&disc_dir)))?;
extract_file(&header.as_bytes()[..0x100], &disc_dir.join("header.bin"), quiet)?; extract_file(&disc_header.as_bytes()[..0x100], &disc_dir.join("header.bin"), quiet)?;
if let Some(region) = data.raw_region.as_deref() { if let Some(region) = disc.region() {
extract_file(region, &disc_dir.join("region.bin"), quiet)?; extract_file(region, &disc_dir.join("region.bin"), quiet)?;
} }
if let Some(ticket) = data.raw_ticket.as_deref() { if let Some(ticket) = data.raw_ticket.as_deref() {