From ca931b652244857d323581b847992ec359a17293 Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Sat, 11 Apr 2026 21:01:16 +0000 Subject: [PATCH] Doc comments on public API, README version fix, format string cleanup --- src/aacs/keydb.rs | 4 ++-- src/aacs/keys.rs | 8 +++---- src/clpi.rs | 2 +- src/drive/capture.rs | 1 + src/drive/mod.rs | 4 ++++ src/error.rs | 52 ++++++++++++++++++++++++++++++++++++-------- src/keydb.rs | 1 + src/mux/ebml.rs | 22 +++++++++---------- 8 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/aacs/keydb.rs b/src/aacs/keydb.rs index 21adcc9..2d509e9 100644 --- a/src/aacs/keydb.rs +++ b/src/aacs/keydb.rs @@ -167,7 +167,7 @@ impl KeyDb { .to_string(); // Try with 0x prefix and without self.disc_entries - .get(&format!("0x{}", hash)) + .get(&format!("0x{hash}")) .or_else(|| self.disc_entries.get(&hash)) .and_then(|e| e.vuk) } @@ -180,7 +180,7 @@ impl KeyDb { .trim_start_matches("0x") .to_string(); self.disc_entries - .get(&format!("0x{}", hash)) + .get(&format!("0x{hash}")) .or_else(|| self.disc_entries.get(&hash)) } diff --git a/src/aacs/keys.rs b/src/aacs/keys.rs index 02b3e66..2c6e699 100644 --- a/src/aacs/keys.rs +++ b/src/aacs/keys.rs @@ -55,7 +55,7 @@ pub fn disc_hash_hex(hash: &[u8; 20]) -> String { let mut s = String::with_capacity(42); s.push_str("0x"); for b in hash { - s.push_str(&format!("{:02X}", b)); + s.push_str(&format!("{b:02X}")); } s } @@ -341,7 +341,7 @@ fn aesg3(key: &[u8; 16], inc: u8) -> [u8; 16] { /// Compute v_mask from a UV value. fn calc_v_mask(uv: u32) -> u32 { - let mut v_mask: u32 = 0xFFFFFFFF; + let mut v_mask: u32 = 0xFFFF_FFFF; while (uv & !v_mask) == 0 && v_mask != 0 { v_mask <<= 1; } @@ -411,7 +411,7 @@ pub fn derive_media_key_from_dk(mkb: &[u8], device_keys: &[DeviceKey]) -> Option continue; } - let u_mask: u32 = 0xFFFFFFFF << u_mask_shift; + let u_mask: u32 = 0xFFFF_FFFF << u_mask_shift; let v_mask = calc_v_mask(uv); if ((device_number & u_mask) == (uv & u_mask)) @@ -419,7 +419,7 @@ pub fn derive_media_key_from_dk(mkb: &[u8], device_keys: &[DeviceKey]) -> Option { // Found matching subset-difference — find the right device key let dev_key_v_mask = calc_v_mask(dk.uv); - let dev_key_u_mask: u32 = 0xFFFFFFFF << dk.u_mask_shift; + let dev_key_u_mask: u32 = 0xFFFF_FFFF << dk.u_mask_shift; if u_mask == dev_key_u_mask && (uv & dev_key_v_mask) == (dk.uv & dev_key_v_mask) { // Derive processing key via tree traversal diff --git a/src/clpi.rs b/src/clpi.rs index add59ec..9c3f1b0 100644 --- a/src/clpi.rs +++ b/src/clpi.rs @@ -46,7 +46,7 @@ impl ClipInfo { /// Reconstruct full SPN from coarse + fine entry. pub fn full_spn(coarse: &EpCoarse, fine: &EpFine) -> u32 { - (coarse.spn_coarse & 0xFFFE0000) + fine.spn_fine + (coarse.spn_coarse & 0xFFFE_0000) + fine.spn_fine } /// Get all EP entries as (PTS, SPN) pairs, fully resolved. diff --git a/src/drive/capture.rs b/src/drive/capture.rs index f3d98f3..6785c74 100644 --- a/src/drive/capture.rs +++ b/src/drive/capture.rs @@ -22,6 +22,7 @@ pub struct DriveCapture { pub rb_mode6: Option>, } +/// A single GET CONFIGURATION feature response from the drive. #[derive(Debug, Clone)] pub struct CapturedFeature { pub code: u16, diff --git a/src/drive/mod.rs b/src/drive/mod.rs index 6d22331..149b91f 100644 --- a/src/drive/mod.rs +++ b/src/drive/mod.rs @@ -24,6 +24,7 @@ use crate::scsi::ScsiTransport; use crate::sector::SectorReader; use std::path::Path; +/// Optical disc drive session -- open, identify, unlock, and read. pub struct DriveSession { scsi: Box, driver: Option>, @@ -257,6 +258,7 @@ impl SectorReader for DriveSession { } } +/// Find all optical drives connected to this system. pub fn find_drives() -> Vec<(String, DriveId)> { #[cfg(target_os = "linux")] { @@ -272,10 +274,12 @@ pub fn find_drives() -> Vec<(String, DriveId)> { } } +/// Find the first optical drive, returning its device path. pub fn find_drive() -> Option { find_drives().into_iter().next().map(|(path, _)| path) } +/// Resolve a device path to its raw SCSI device, with optional warning message. pub fn resolve_device(path: &str) -> Result<(String, Option)> { #[cfg(target_os = "linux")] { diff --git a/src/error.rs b/src/error.rs index b8e1df4..7f485db 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,102 +71,135 @@ pub const E_MUX_WRITE: u16 = 9001; /// Structured error with numeric code and context data. No English text. #[derive(Debug)] pub enum Error { - // Device + /// Device not found at the given path. DeviceNotFound { path: String, }, + /// Insufficient permissions to open the device. DevicePermission { path: String, }, - // Profile + /// Drive model is not in the profile database. UnsupportedDrive { vendor_id: String, product_id: String, product_revision: String, }, + /// No matching firmware profile found for this drive revision. ProfileNotFound { vendor_id: String, product_revision: String, vendor_specific: String, }, + /// Failed to parse the bundled profile database. ProfileParse, - // Unlock + /// Drive unlock (firmware upload) failed. UnlockFailed, + /// Firmware signature verification failed. SignatureMismatch { expected: [u8; 4], got: [u8; 4], }, + /// Operation requires an unlocked drive. NotUnlocked, + /// Operation requires a calibrated drive. NotCalibrated, - // SCSI + /// SCSI command returned an error status. ScsiError { opcode: u8, status: u8, sense_key: u8, }, + /// SCSI command timed out. ScsiTimeout { opcode: u8, }, - // I/O + /// Underlying I/O error. IoError { source: std::io::Error, }, + /// Write operation failed. WriteError, - // Disc format + /// Failed to read disc sector. DiscRead { sector: u64, }, + /// MPLS playlist parsing failed. MplsParse, + /// CLPI clip info parsing failed. ClpiParse, + /// File not found on the UDF filesystem. UdfNotFound { path: String, }, + /// Disc contains no playable titles. DiscNoTitles, + /// Title index out of range. DiscTitleRange { index: usize, count: usize, }, + /// Title has no sector extents to read. DiscNoExtents, + /// DVD IFO file parsing failed. IfoParse, - // AACS + /// No AACS decryption keys available for this disc. AacsNoKeys, + /// Host certificate too short. AacsCertShort, + /// Failed to allocate AGID for AACS handshake. AacsAgidAlloc, + /// Drive rejected the host certificate. AacsCertRejected, + /// Failed to read drive certificate. AacsCertRead, + /// Drive certificate verification failed. AacsCertVerify, + /// Failed to read host key from drive. AacsKeyRead, + /// Drive rejected the host key. AacsKeyRejected, + /// Host key verification failed. AacsKeyVerify, + /// Failed to read Volume ID. AacsVidRead, + /// Volume ID MAC verification failed. AacsVidMac, + /// Failed to derive the data key. AacsDataKey, + /// Failed to derive the Volume Unique Key. AacsVukDerive, - // Keydb + /// Failed to connect to the KEYDB server. KeydbConnect { host: String, }, + /// KEYDB server returned an HTTP error. KeydbHttp { status: u16, }, + /// Downloaded KEYDB file is invalid (no entries found). KeydbInvalid, + /// Failed to write KEYDB to disk. KeydbWrite { path: String, }, + /// Failed to parse KEYDB file. KeydbParse, + /// Failed to load KEYDB from disk. KeydbLoad { path: String, }, - // Mux + /// Lookahead buffer exhausted before codec headers found. MuxLookahead, + /// Muxer write failed. MuxWrite, } @@ -306,4 +339,5 @@ impl From for Error { } } +/// Convenience alias for `Result`. pub type Result = std::result::Result; diff --git a/src/keydb.rs b/src/keydb.rs index 0b934fe..b53d229 100644 --- a/src/keydb.rs +++ b/src/keydb.rs @@ -71,6 +71,7 @@ pub fn save(data: &[u8]) -> Result { }) } +/// Result of a KEYDB update -- path written, entry count, and byte size. #[derive(Debug)] pub struct UpdateResult { pub path: PathBuf, diff --git a/src/mux/ebml.rs b/src/mux/ebml.rs index e6ff096..06069b2 100644 --- a/src/mux/ebml.rs +++ b/src/mux/ebml.rs @@ -203,7 +203,7 @@ pub fn read_size(r: &mut impl Read) -> io::Result<(u64, usize)> { let mut b = [0u8; 2]; r.read_exact(&mut b)?; let val = (((b0 & 0x1F) as u64) << 16) | (b[0] as u64) << 8 | b[1] as u64; - if val == 0x1FFFFF { + if val == 0x1F_FFFF { return Ok((u64::MAX, 3)); } Ok((val, 3)) @@ -212,7 +212,7 @@ pub fn read_size(r: &mut impl Read) -> io::Result<(u64, usize)> { r.read_exact(&mut b)?; let val = (((b0 & 0x0F) as u64) << 24) | (b[0] as u64) << 16 | (b[1] as u64) << 8 | b[2] as u64; - if val == 0x0FFFFFFF { + if val == 0x0FFF_FFFF { return Ok((u64::MAX, 4)); } Ok((val, 4)) @@ -256,7 +256,7 @@ pub fn read_size(r: &mut impl Read) -> io::Result<(u64, usize)> { | (b[4] as u64) << 16 | (b[5] as u64) << 8 | b[6] as u64; - if val == 0x00FFFFFFFFFFFFFF { + if val == 0x00FF_FFFF_FFFF_FFFF { return Ok((u64::MAX, 8)); } Ok((val, 8)) @@ -336,7 +336,7 @@ pub fn read_vint(r: &mut impl Read) -> io::Result<(u64, usize)> { // ============================================================ // EBML Header -pub const EBML: u32 = 0x1A45DFA3; +pub const EBML: u32 = 0x1A45_DFA3; pub const EBML_VERSION: u32 = 0x4286; pub const EBML_READ_VERSION: u32 = 0x42F7; pub const EBML_MAX_ID_LENGTH: u32 = 0x42F2; @@ -346,16 +346,16 @@ pub const EBML_DOC_TYPE_VERSION: u32 = 0x4287; pub const EBML_DOC_TYPE_READ_VERSION: u32 = 0x4285; // Segment -pub const SEGMENT: u32 = 0x18538067; +pub const SEGMENT: u32 = 0x1853_8067; // Seek Head -pub const SEEK_HEAD: u32 = 0x114D9B74; +pub const SEEK_HEAD: u32 = 0x114D_9B74; pub const SEEK: u32 = 0x4DBB; pub const SEEK_ID: u32 = 0x53AB; pub const SEEK_POSITION: u32 = 0x53AC; // Segment Info -pub const INFO: u32 = 0x1549A966; +pub const INFO: u32 = 0x1549_A966; pub const TIMESTAMP_SCALE: u32 = 0x2AD7B1; pub const DURATION: u32 = 0x4489; pub const MUXING_APP: u32 = 0x4D80; @@ -363,7 +363,7 @@ pub const WRITING_APP: u32 = 0x5741; pub const TITLE: u32 = 0x7BA9; // Tracks -pub const TRACKS: u32 = 0x1654AE6B; +pub const TRACKS: u32 = 0x1654_AE6B; pub const TRACK_ENTRY: u32 = 0xAE; pub const TRACK_NUMBER: u32 = 0xD7; pub const TRACK_UID: u32 = 0x73C5; @@ -396,12 +396,12 @@ pub const CHANNELS: u32 = 0x9F; pub const BIT_DEPTH: u32 = 0x6264; // Cluster -pub const CLUSTER: u32 = 0x1F43B675; +pub const CLUSTER: u32 = 0x1F43_B675; pub const CLUSTER_TIMESTAMP: u32 = 0xE7; pub const SIMPLE_BLOCK: u32 = 0xA3; // Cues -pub const CUES: u32 = 0x1C53BB6B; +pub const CUES: u32 = 0x1C53_BB6B; pub const CUE_POINT: u32 = 0xBB; pub const CUE_TIME: u32 = 0xB3; pub const CUE_TRACK_POSITIONS: u32 = 0xB7; @@ -409,7 +409,7 @@ pub const CUE_TRACK: u32 = 0xF7; pub const CUE_CLUSTER_POSITION: u32 = 0xF1; // Chapters -pub const CHAPTERS: u32 = 0x1043A770; +pub const CHAPTERS: u32 = 0x1043_A770; pub const EDITION_ENTRY: u32 = 0x45B9; pub const CHAPTER_ATOM: u32 = 0xB6; pub const CHAPTER_UID: u32 = 0x73C4;