1#![warn(missing_docs, clippy::missing_inline_in_public_items)]
2use std::{
62 io::{BufRead, Read, Seek},
63 path::Path,
64};
65
66pub use disc::{
67 ApploaderHeader, DiscHeader, DolHeader, FileStream, Fst, Node, NodeKind, OwnedFileStream,
68 PartitionBase, PartitionHeader, PartitionKind, PartitionMeta, SignedHeader, Ticket,
69 TicketLimit, TmdHeader, WindowedStream, BI2_SIZE, BOOT_SIZE, DL_DVD_SIZE, GCN_MAGIC,
70 MINI_DVD_SIZE, REGION_SIZE, SECTOR_SIZE, SL_DVD_SIZE, WII_MAGIC,
71};
72pub use io::{
73 block::{DiscStream, PartitionInfo},
74 Compression, DiscMeta, Format, KeyBytes, MagicBytes,
75};
76pub use util::lfg::LaggedFibonacci;
77
78mod disc;
79mod io;
80mod util;
81
82#[derive(thiserror::Error, Debug)]
84pub enum Error {
85 #[error("disc format error: {0}")]
87 DiscFormat(String),
88 #[error("I/O error: {0}")]
90 Io(String, #[source] std::io::Error),
91 #[error("error: {0}")]
93 Other(String),
94 #[error("allocation failed")]
96 Alloc(zerocopy::AllocError),
97}
98
99impl From<&str> for Error {
100 #[inline]
101 fn from(s: &str) -> Error { Error::Other(s.to_string()) }
102}
103
104impl From<String> for Error {
105 #[inline]
106 fn from(s: String) -> Error { Error::Other(s) }
107}
108
109impl From<zerocopy::AllocError> for Error {
110 #[inline]
111 fn from(e: zerocopy::AllocError) -> Error { Error::Alloc(e) }
112}
113
114pub type Result<T, E = Error> = core::result::Result<T, E>;
116
117pub trait ErrorContext {
119 fn context(self, context: impl Into<String>) -> Error;
121}
122
123impl ErrorContext for std::io::Error {
124 #[inline]
125 fn context(self, context: impl Into<String>) -> Error { Error::Io(context.into(), self) }
126}
127
128pub trait ResultContext<T> {
130 fn context(self, context: impl Into<String>) -> Result<T>;
132
133 fn with_context<F>(self, f: F) -> Result<T>
135 where F: FnOnce() -> String;
136}
137
138impl<T, E> ResultContext<T> for Result<T, E>
139where E: ErrorContext
140{
141 #[inline]
142 fn context(self, context: impl Into<String>) -> Result<T> {
143 self.map_err(|e| e.context(context))
144 }
145
146 #[inline]
147 fn with_context<F>(self, f: F) -> Result<T>
148 where F: FnOnce() -> String {
149 self.map_err(|e| e.context(f()))
150 }
151}
152
153#[derive(Default, Debug, Clone)]
155pub struct OpenOptions {
156 pub rebuild_encryption: bool,
159 pub validate_hashes: bool,
161}
162
163pub struct Disc {
167 reader: disc::reader::DiscReader,
168 options: OpenOptions,
169}
170
171impl Disc {
172 #[inline]
174 pub fn new<P: AsRef<Path>>(path: P) -> Result<Disc> {
175 Disc::new_with_options(path, &OpenOptions::default())
176 }
177
178 #[inline]
180 pub fn new_with_options<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<Disc> {
181 let io = io::block::open(path.as_ref())?;
182 let reader = disc::reader::DiscReader::new(io, options)?;
183 Ok(Disc { reader, options: options.clone() })
184 }
185
186 #[inline]
188 pub fn new_stream(stream: Box<dyn DiscStream>) -> Result<Disc> {
189 Disc::new_stream_with_options(stream, &OpenOptions::default())
190 }
191
192 #[inline]
194 pub fn new_stream_with_options(
195 stream: Box<dyn DiscStream>,
196 options: &OpenOptions,
197 ) -> Result<Disc> {
198 let io = io::block::new(stream)?;
199 let reader = disc::reader::DiscReader::new(io, options)?;
200 Ok(Disc { reader, options: options.clone() })
201 }
202
203 #[inline]
205 pub fn detect<R>(stream: &mut R) -> std::io::Result<Option<Format>>
206 where R: Read + ?Sized {
207 io::block::detect(stream)
208 }
209
210 #[inline]
212 pub fn header(&self) -> &DiscHeader { self.reader.header() }
213
214 #[inline]
218 pub fn region(&self) -> Option<&[u8; REGION_SIZE]> { self.reader.region() }
219
220 #[inline]
222 pub fn meta(&self) -> DiscMeta { self.reader.meta() }
223
224 #[inline]
226 pub fn disc_size(&self) -> u64 { self.reader.disc_size() }
227
228 #[inline]
232 pub fn partitions(&self) -> &[PartitionInfo] { self.reader.partitions() }
233
234 #[inline]
238 pub fn open_partition(&self, index: usize) -> Result<Box<dyn PartitionBase>> {
239 self.reader.open_partition(index, &self.options)
240 }
241
242 #[inline]
247 pub fn open_partition_kind(&self, kind: PartitionKind) -> Result<Box<dyn PartitionBase>> {
248 self.reader.open_partition_kind(kind, &self.options)
249 }
250}
251
252impl BufRead for Disc {
253 #[inline]
254 fn fill_buf(&mut self) -> std::io::Result<&[u8]> { self.reader.fill_buf() }
255
256 #[inline]
257 fn consume(&mut self, amt: usize) { self.reader.consume(amt) }
258}
259
260impl Read for Disc {
261 #[inline]
262 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { self.reader.read(buf) }
263}
264
265impl Seek for Disc {
266 #[inline]
267 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> { self.reader.seek(pos) }
268}