Use full LTO, update dependencies & CI

This commit is contained in:
2024-09-08 16:29:48 -06:00
parent a8f91ff9c2
commit d2b8135cdb
10 changed files with 292 additions and 172 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "nod"
version = "1.2.0"
version = "1.2.1"
edition = "2021"
rust-version = "1.73.0"
authors = ["Luke Street <luke@street.dev>"]
@@ -31,11 +31,11 @@ cbc = "0.1"
digest = "0.10"
dyn-clone = "1.0"
encoding_rs = "0.8"
itertools = "0.12"
liblzma = { version = "0.2", features = ["static"], optional = true }
itertools = "0.13"
liblzma = { version = "0.3", features = ["static"], optional = true }
log = "0.4"
miniz_oxide = { version = "0.7", optional = true }
rayon = "1.8"
miniz_oxide = { version = "0.8", optional = true }
rayon = "1.10"
sha1 = "0.10"
thiserror = "1.0"
zerocopy = { version = "0.7", features = ["alloc", "derive"] }

View File

@@ -64,9 +64,11 @@ static_assert!(size_of::<DiscHeader>() == 0x400);
impl DiscHeader {
/// Game ID as a string.
#[inline]
pub fn game_id_str(&self) -> &str { from_utf8(&self.game_id).unwrap_or("[invalid]") }
/// Game title as a string.
#[inline]
pub fn game_title_str(&self) -> &str {
CStr::from_bytes_until_nul(&self.game_title)
.ok()
@@ -75,9 +77,11 @@ impl DiscHeader {
}
/// Whether this is a GameCube disc.
#[inline]
pub fn is_gamecube(&self) -> bool { self.gcn_magic.get() == 0xC2339F3D }
/// Whether this is a Wii disc.
#[inline]
pub fn is_wii(&self) -> bool { self.wii_magic.get() == 0x5D1C9EA3 }
}
@@ -117,6 +121,7 @@ static_assert!(size_of::<PartitionHeader>() == 0x40);
impl PartitionHeader {
/// Offset within the partition to the main DOL.
#[inline]
pub fn dol_offset(&self, is_wii: bool) -> u64 {
if is_wii {
self.dol_offset.get() as u64 * 4
@@ -126,6 +131,7 @@ impl PartitionHeader {
}
/// Offset within the partition to the file system table (FST).
#[inline]
pub fn fst_offset(&self, is_wii: bool) -> u64 {
if is_wii {
self.fst_offset.get() as u64 * 4
@@ -135,6 +141,7 @@ impl PartitionHeader {
}
/// Size of the file system table (FST).
#[inline]
pub fn fst_size(&self, is_wii: bool) -> u64 {
if is_wii {
self.fst_size.get() as u64 * 4
@@ -144,6 +151,7 @@ impl PartitionHeader {
}
/// Maximum size of the file system table (FST) across multi-disc games.
#[inline]
pub fn fst_max_size(&self, is_wii: bool) -> u64 {
if is_wii {
self.fst_max_size.get() as u64 * 4
@@ -171,6 +179,7 @@ pub struct ApploaderHeader {
impl ApploaderHeader {
/// Apploader build date as a string.
#[inline]
pub fn date_str(&self) -> Option<&str> {
CStr::from_bytes_until_nul(&self.date).ok().and_then(|c| c.to_str().ok())
}
@@ -222,6 +231,7 @@ pub enum PartitionKind {
}
impl Display for PartitionKind {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Data => write!(f, "Data"),
@@ -237,6 +247,7 @@ impl Display for PartitionKind {
impl PartitionKind {
/// Returns the directory name for the partition kind.
#[inline]
pub fn dir_name(&self) -> Cow<str> {
match self {
Self::Data => Cow::Borrowed("DATA"),
@@ -251,6 +262,7 @@ impl PartitionKind {
}
impl From<u32> for PartitionKind {
#[inline]
fn from(v: u32) -> Self {
match v {
0 => Self::Data,
@@ -334,32 +346,39 @@ pub struct PartitionMeta {
impl PartitionMeta {
/// A view into the disc header.
#[inline]
pub fn header(&self) -> &DiscHeader {
DiscHeader::ref_from(&self.raw_boot[..size_of::<DiscHeader>()]).unwrap()
}
/// A view into the partition header.
#[inline]
pub fn partition_header(&self) -> &PartitionHeader {
PartitionHeader::ref_from(&self.raw_boot[size_of::<DiscHeader>()..]).unwrap()
}
/// A view into the apploader header.
#[inline]
pub fn apploader_header(&self) -> &ApploaderHeader {
ApploaderHeader::ref_from_prefix(&self.raw_apploader).unwrap()
}
/// A view into the file system table (FST).
#[inline]
pub fn fst(&self) -> Result<Fst, &'static str> { Fst::new(&self.raw_fst) }
/// A view into the DOL header.
#[inline]
pub fn dol_header(&self) -> &DolHeader { DolHeader::ref_from_prefix(&self.raw_dol).unwrap() }
/// A view into the ticket. (Wii only)
#[inline]
pub fn ticket(&self) -> Option<&Ticket> {
self.raw_ticket.as_ref().and_then(|v| Ticket::ref_from(v))
}
/// A view into the TMD. (Wii only)
#[inline]
pub fn tmd_header(&self) -> Option<&TmdHeader> {
self.raw_tmd.as_ref().and_then(|v| TmdHeader::ref_from_prefix(v))
}

View File

@@ -33,6 +33,7 @@ static_assert!(size_of::<Node>() == 12);
impl Node {
/// File system node kind.
#[inline]
pub fn kind(&self) -> NodeKind {
match self.kind {
0 => NodeKind::File,
@@ -42,12 +43,15 @@ impl Node {
}
/// Whether the node is a file.
#[inline]
pub fn is_file(&self) -> bool { self.kind == 0 }
/// Whether the node is a directory.
#[inline]
pub fn is_dir(&self) -> bool { self.kind == 1 }
/// Offset in the string table to the filename.
#[inline]
pub fn name_offset(&self) -> u32 {
u32::from_be_bytes([0, self.name_offset[0], self.name_offset[1], self.name_offset[2]])
}
@@ -55,6 +59,7 @@ impl Node {
/// For files, this is the partition offset of the file data. (Wii: >> 2)
///
/// For directories, this is the parent node index in the FST.
#[inline]
pub fn offset(&self, is_wii: bool) -> u64 {
if is_wii && self.kind == 0 {
self.offset.get() as u64 * 4
@@ -68,6 +73,7 @@ impl Node {
/// For directories, this is the child end index in the FST.
///
/// Number of child files and directories recursively is `length - offset`.
#[inline]
pub fn length(&self) -> u64 { self.length.get() as u64 }
}
@@ -81,6 +87,7 @@ pub struct Fst<'a> {
impl<'a> Fst<'a> {
/// Create a new FST view from a buffer.
#[allow(clippy::missing_inline_in_public_items)]
pub fn new(buf: &'a [u8]) -> Result<Self, &'static str> {
let Some(root_node) = Node::ref_from_prefix(buf) else {
return Err("FST root node not found");
@@ -96,9 +103,11 @@ impl<'a> Fst<'a> {
}
/// Iterate over the nodes in the FST.
#[inline]
pub fn iter(&self) -> FstIter { FstIter { fst: self, idx: 1 } }
/// Get the name of a node.
#[allow(clippy::missing_inline_in_public_items)]
pub fn get_name(&self, node: &Node) -> Result<Cow<str>, String> {
let name_buf = self.string_table.get(node.name_offset() as usize..).ok_or_else(|| {
format!(
@@ -118,6 +127,7 @@ impl<'a> Fst<'a> {
}
/// Finds a particular file or directory by path.
#[allow(clippy::missing_inline_in_public_items)]
pub fn find(&self, path: &str) -> Option<(usize, &Node)> {
let mut split = path.trim_matches('/').split('/');
let mut current = split.next()?;

View File

@@ -46,6 +46,7 @@ pub enum Format {
}
impl fmt::Display for Format {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Format::Iso => write!(f, "ISO"),
@@ -81,6 +82,7 @@ pub enum Compression {
}
impl fmt::Display for Compression {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Compression::None => write!(f, "None"),

View File

@@ -1,4 +1,4 @@
#![warn(missing_docs)]
#![warn(missing_docs, clippy::missing_inline_in_public_items)]
//! Library for traversing & reading Nintendo Optical Disc (GameCube and Wii) images.
//!
//! Originally based on the C++ library [nod](https://github.com/AxioDL/nod),
@@ -92,10 +92,12 @@ pub enum Error {
}
impl From<&str> for Error {
#[inline]
fn from(s: &str) -> Error { Error::Other(s.to_string()) }
}
impl From<String> for Error {
#[inline]
fn from(s: String) -> Error { Error::Other(s) }
}
@@ -109,6 +111,7 @@ pub trait ErrorContext {
}
impl ErrorContext for std::io::Error {
#[inline]
fn context(self, context: impl Into<String>) -> Error { Error::Io(context.into(), self) }
}
@@ -125,10 +128,12 @@ pub trait ResultContext<T> {
impl<T, E> ResultContext<T> for Result<T, E>
where E: ErrorContext
{
#[inline]
fn context(self, context: impl Into<String>) -> Result<T> {
self.map_err(|e| e.context(context))
}
#[inline]
fn with_context<F>(self, f: F) -> Result<T>
where F: FnOnce() -> String {
self.map_err(|e| e.context(f()))
@@ -155,11 +160,13 @@ pub struct Disc {
impl Disc {
/// Opens a disc image from a file path.
#[inline]
pub fn new<P: AsRef<Path>>(path: P) -> Result<Disc> {
Disc::new_with_options(path, &OpenOptions::default())
}
/// Opens a disc image from a file path with custom options.
#[inline]
pub fn new_with_options<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<Disc> {
let io = io::block::open(path.as_ref())?;
let reader = disc::reader::DiscReader::new(io, options)?;
@@ -167,22 +174,27 @@ impl Disc {
}
/// The disc's primary header.
#[inline]
pub fn header(&self) -> &DiscHeader { self.reader.header() }
/// Returns extra metadata included in the disc file format, if any.
#[inline]
pub fn meta(&self) -> DiscMeta { self.reader.meta() }
/// The disc's size in bytes, or an estimate if not stored by the format.
#[inline]
pub fn disc_size(&self) -> u64 { self.reader.disc_size() }
/// A list of Wii partitions on the disc.
///
/// **GameCube**: This will return an empty slice.
#[inline]
pub fn partitions(&self) -> &[PartitionInfo] { self.reader.partitions() }
/// Opens a decrypted partition read stream for the specified partition index.
///
/// **GameCube**: `index` must always be 0.
#[inline]
pub fn open_partition(&self, index: usize) -> Result<Box<dyn PartitionBase>> {
self.reader.open_partition(index, &self.options)
}
@@ -191,15 +203,18 @@ impl Disc {
/// the specified kind.
///
/// **GameCube**: `kind` must always be [`PartitionKind::Data`].
#[inline]
pub fn open_partition_kind(&self, kind: PartitionKind) -> Result<Box<dyn PartitionBase>> {
self.reader.open_partition_kind(kind, &self.options)
}
}
impl Read for Disc {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { self.reader.read(buf) }
}
impl Seek for Disc {
#[inline]
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> { self.reader.seek(pos) }
}

View File

@@ -10,6 +10,7 @@ pub trait ReadStream: Read + Seek {
/// Creates a windowed read sub-stream with offset and size.
///
/// Seeks underlying stream immediately.
#[inline]
fn new_window(&mut self, offset: u64, size: u64) -> io::Result<SharedWindowedReadStream> {
self.seek(SeekFrom::Start(offset))?;
Ok(SharedWindowedReadStream { base: self.as_dyn(), begin: offset, end: offset + size })
@@ -22,6 +23,7 @@ pub trait ReadStream: Read + Seek {
impl<T> ReadStream for T
where T: Read + Seek
{
#[inline]
fn as_dyn(&mut self) -> &mut dyn ReadStream { self }
}