diff --git a/Cargo.lock b/Cargo.lock index 1edde6d..476ec30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -34,18 +34,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy 0.7.35", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -395,7 +383,7 @@ dependencies = [ [[package]] name = "decomp-toolkit" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "ar", @@ -447,7 +435,7 @@ dependencies = [ "tracing-attributes", "tracing-subscriber", "xxhash-rust", - "zerocopy 0.8.0", + "zerocopy", ] [[package]] @@ -577,6 +565,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "generic-array" version = "0.14.7" @@ -598,9 +592,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -608,20 +602,14 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "foldhash", +] [[package]] name = "heck" @@ -672,7 +660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown", ] [[package]] @@ -918,9 +906,9 @@ dependencies = [ [[package]] name = "nod" -version = "1.4.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc8822a8dac202a8589b1c49c7916bf8dec0adb8e8048f4f5cb979f9f4bc5e9" +checksum = "89c1186fb8e051c71e3f7b0b67bc2ab4e82c34c59d4fcc91d58ec2a030a9aff5" dependencies = [ "adler", "aes", @@ -937,15 +925,15 @@ dependencies = [ "rayon", "sha1", "thiserror", - "zerocopy 0.8.0", + "zerocopy", "zstd", ] [[package]] name = "nodtool" -version = "1.4.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f838d5c364ae3f04fbec424baa64facadb401b8114a6b1f96ea35b2d99f5e13" +checksum = "331755fd6f5f07bcafa5a5bde23dcdf3a68aa34b922bfa17af8a16a4669cbe15" dependencies = [ "argp", "base16ct", @@ -968,7 +956,7 @@ dependencies = [ "tracing-attributes", "tracing-subscriber", "xxhash-rust", - "zerocopy 0.8.0", + "zerocopy", "zstd", ] @@ -1046,12 +1034,12 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "crc32fast", - "hashbrown 0.14.5", + "hashbrown", "indexmap", "memchr", ] @@ -2154,38 +2142,18 @@ checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "dd2e5ce961dea177d282ec084dca2aa411b7411199a68d79eb1beacb305a6cd9" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7885ffcb82507a0f213c593e77c5f13d12cb96588d4e835ad7e9423ba034db" -dependencies = [ - "zerocopy-derive 0.8.0", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930ad75608219e8ffdb8962a5433cb2b30064c7ccb564d3b76c2963390b1e435" +checksum = "b06304eeddb6081af98ac59db08c868ac197e586086b996d15a86ed70e09a754" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 773e3b3..bacba3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "decomp-toolkit" description = "Yet another GameCube/Wii decompilation toolkit." authors = ["Luke Street "] license = "MIT OR Apache-2.0" -version = "1.1.0" +version = "1.1.1" edition = "2021" publish = false repository = "https://github.com/encounter/decomp-toolkit" diff --git a/src/vfs/disc.rs b/src/vfs/disc.rs index 366dbc4..c65e0ab 100644 --- a/src/vfs/disc.rs +++ b/src/vfs/disc.rs @@ -7,8 +7,9 @@ use std::{ use filetime::FileTime; use nodtool::{ nod, - nod::{DiscStream, Fst, NodeKind, OwnedFileStream, PartitionBase, PartitionMeta}, + nod::{Disc, DiscStream, Fst, NodeKind, OwnedFileStream, PartitionBase, PartitionMeta}, }; +use zerocopy::IntoBytes; use super::{ next_non_empty, StaticFile, Vfs, VfsError, VfsFile, VfsFileType, VfsMetadata, VfsResult, @@ -16,72 +17,100 @@ use super::{ #[derive(Clone)] pub struct DiscFs { + disc: Arc, base: Box, meta: Box, mtime: Option, } -enum DiscNode<'a> { - None, +enum SpecialDir { Root, Sys, + Disc, +} + +enum DiscNode<'a> { + None, + Special(SpecialDir), Node(Fst<'a>, usize, nod::Node), Static(&'a [u8]), } impl DiscFs { - pub fn new(mut base: Box, mtime: Option) -> io::Result { + pub fn new( + disc: Arc, + mut base: Box, + mtime: Option, + ) -> io::Result { let meta = base.meta().map_err(nod_to_io_error)?; - Ok(Self { base, meta, mtime }) + Ok(Self { disc, base, meta, mtime }) } fn find(&self, path: &str) -> VfsResult { let path = path.trim_matches('/'); let mut split = path.split('/'); let mut segment = next_non_empty(&mut split); - if segment.is_empty() { - return Ok(DiscNode::Root); - } - if segment.eq_ignore_ascii_case("files") { - let fst = Fst::new(&self.meta.raw_fst)?; - if next_non_empty(&mut split).is_empty() { - let root = fst.nodes[0]; - return Ok(DiscNode::Node(fst, 0, root)); - } - let remainder = &path[segment.len() + 1..]; - match fst.find(remainder) { - Some((idx, node)) => Ok(DiscNode::Node(fst, idx, node)), - None => Ok(DiscNode::None), - } - } else if segment.eq_ignore_ascii_case("sys") { - segment = next_non_empty(&mut split); - // No directories in sys - if split.next().is_some() { - return Ok(DiscNode::None); - } - match segment.to_ascii_lowercase().as_str() { - "" => Ok(DiscNode::Sys), - "boot.bin" => Ok(DiscNode::Static(self.meta.raw_boot.as_slice())), - "bi2.bin" => Ok(DiscNode::Static(self.meta.raw_bi2.as_slice())), - "apploader.bin" => Ok(DiscNode::Static(self.meta.raw_apploader.as_ref())), - "fst.bin" => Ok(DiscNode::Static(self.meta.raw_fst.as_ref())), - "main.dol" => Ok(DiscNode::Static(self.meta.raw_dol.as_ref())), - "ticket.bin" => { - Ok(DiscNode::Static(self.meta.raw_ticket.as_deref().ok_or(VfsError::NotFound)?)) + match segment.to_ascii_lowercase().as_str() { + "" => Ok(DiscNode::Special(SpecialDir::Root)), + "files" => { + let fst = Fst::new(&self.meta.raw_fst)?; + if next_non_empty(&mut split).is_empty() { + let root = fst.nodes[0]; + return Ok(DiscNode::Node(fst, 0, root)); } - "tmd.bin" => { - Ok(DiscNode::Static(self.meta.raw_tmd.as_deref().ok_or(VfsError::NotFound)?)) + let remainder = &path[segment.len() + 1..]; + match fst.find(remainder) { + Some((idx, node)) => Ok(DiscNode::Node(fst, idx, node)), + None => Ok(DiscNode::None), } - "cert.bin" => Ok(DiscNode::Static( - self.meta.raw_cert_chain.as_deref().ok_or(VfsError::NotFound)?, - )), - "h3.bin" => Ok(DiscNode::Static( - self.meta.raw_h3_table.as_deref().ok_or(VfsError::NotFound)?, - )), - _ => Ok(DiscNode::None), } - } else { - return Ok(DiscNode::None); + "sys" => { + segment = next_non_empty(&mut split); + // No directories in sys + if !next_non_empty(&mut split).is_empty() { + return Ok(DiscNode::None); + } + match segment.to_ascii_lowercase().as_str() { + "" => Ok(DiscNode::Special(SpecialDir::Sys)), + "boot.bin" => Ok(DiscNode::Static(self.meta.raw_boot.as_slice())), + "bi2.bin" => Ok(DiscNode::Static(self.meta.raw_bi2.as_slice())), + "apploader.img" => Ok(DiscNode::Static(self.meta.raw_apploader.as_ref())), + "fst.bin" => Ok(DiscNode::Static(self.meta.raw_fst.as_ref())), + "main.dol" => Ok(DiscNode::Static(self.meta.raw_dol.as_ref())), + _ => Ok(DiscNode::None), + } + } + "disc" => { + if !self.disc.header().is_wii() { + return Ok(DiscNode::None); + } + segment = next_non_empty(&mut split); + // No directories in disc + if !next_non_empty(&mut split).is_empty() { + return Ok(DiscNode::None); + } + match segment.to_ascii_lowercase().as_str() { + "" => Ok(DiscNode::Special(SpecialDir::Disc)), + "header.bin" => Ok(DiscNode::Static(&self.disc.header().as_bytes()[..0x100])), + "region.bin" => { + Ok(DiscNode::Static(self.disc.region().ok_or(VfsError::NotFound)?)) + } + _ => Ok(DiscNode::None), + } + } + "ticket.bin" => { + Ok(DiscNode::Static(self.meta.raw_ticket.as_deref().ok_or(VfsError::NotFound)?)) + } + "tmd.bin" => { + Ok(DiscNode::Static(self.meta.raw_tmd.as_deref().ok_or(VfsError::NotFound)?)) + } + "cert.bin" => { + Ok(DiscNode::Static(self.meta.raw_cert_chain.as_deref().ok_or(VfsError::NotFound)?)) + } + "h3.bin" => { + Ok(DiscNode::Static(self.meta.raw_h3_table.as_deref().ok_or(VfsError::NotFound)?)) + } + _ => Ok(DiscNode::None), } } } @@ -90,8 +119,7 @@ impl Vfs for DiscFs { fn open(&mut self, path: &str) -> VfsResult> { match self.find(path)? { DiscNode::None => Err(VfsError::NotFound), - DiscNode::Root => Err(VfsError::IsADirectory), - DiscNode::Sys => Err(VfsError::IsADirectory), + DiscNode::Special(_) => Err(VfsError::IsADirectory), DiscNode::Node(_, _, node) => match node.kind() { NodeKind::File => { if node.length() > 2048 { @@ -119,28 +147,41 @@ impl Vfs for DiscFs { fn read_dir(&mut self, path: &str) -> VfsResult> { match self.find(path)? { DiscNode::None => Err(VfsError::NotFound), - DiscNode::Root => Ok(vec!["files".to_string(), "sys".to_string()]), - DiscNode::Sys => { - let mut sys = vec![ - "boot.bin".to_string(), - "bi2.bin".to_string(), - "apploader.bin".to_string(), - "fst.bin".to_string(), - "main.dol".to_string(), - ]; - if self.meta.raw_ticket.is_some() { - sys.push("ticket.bin".to_string()); - } - if self.meta.raw_tmd.is_some() { - sys.push("tmd.bin".to_string()); + DiscNode::Special(SpecialDir::Root) => { + let mut entries = vec!["files".to_string(), "sys".to_string()]; + if self.disc.header().is_wii() { + entries.push("disc".to_string()); } if self.meta.raw_cert_chain.is_some() { - sys.push("cert.bin".to_string()); + entries.push("cert.bin".to_string()); } if self.meta.raw_h3_table.is_some() { - sys.push("h3.bin".to_string()); + entries.push("h3.bin".to_string()); } - Ok(sys) + if self.meta.raw_ticket.is_some() { + entries.push("ticket.bin".to_string()); + } + if self.meta.raw_tmd.is_some() { + entries.push("tmd.bin".to_string()); + } + Ok(entries) + } + DiscNode::Special(SpecialDir::Sys) => Ok(vec![ + "boot.bin".to_string(), + "bi2.bin".to_string(), + "apploader.img".to_string(), + "fst.bin".to_string(), + "main.dol".to_string(), + ]), + DiscNode::Special(SpecialDir::Disc) => { + let mut entries = Vec::new(); + if self.disc.header().is_wii() { + entries.push("header.bin".to_string()); + if self.disc.region().is_some() { + entries.push("region.bin".to_string()); + } + } + Ok(entries) } DiscNode::Node(fst, idx, node) => { match node.kind() { @@ -173,7 +214,7 @@ impl Vfs for DiscFs { fn metadata(&mut self, path: &str) -> VfsResult { match self.find(path)? { DiscNode::None => Err(VfsError::NotFound), - DiscNode::Root | DiscNode::Sys => { + DiscNode::Special(_) => { Ok(VfsMetadata { file_type: VfsFileType::Directory, len: 0, mtime: self.mtime }) } DiscNode::Node(_, _, node) => { diff --git a/src/vfs/mod.rs b/src/vfs/mod.rs index c8b34f3..3f8c282 100644 --- a/src/vfs/mod.rs +++ b/src/vfs/mod.rs @@ -325,10 +325,11 @@ pub fn open_fs(mut file: Box, kind: ArchiveKind) -> io::Result Ok(Box::new(RarcFs::new(file)?)), ArchiveKind::U8 => Ok(Box::new(U8Fs::new(file)?)), ArchiveKind::Disc(_) => { - let disc = nod::Disc::new_stream(file.into_disc_stream()).map_err(nod_to_io_error)?; + let disc = + Arc::new(nod::Disc::new_stream(file.into_disc_stream()).map_err(nod_to_io_error)?); let partition = disc.open_partition_kind(nod::PartitionKind::Data).map_err(nod_to_io_error)?; - Ok(Box::new(DiscFs::new(partition, metadata.mtime)?)) + Ok(Box::new(DiscFs::new(disc, partition, metadata.mtime)?)) } } }