style: cargo fmt

This commit is contained in:
2026-04-24 12:23:42 -07:00
parent a67ed2635b
commit 3685c7a878
33 changed files with 127 additions and 99 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
//! AACS content decryption — AES primitives, unit decryption, bus encryption. //! AACS content decryption — AES primitives, unit decryption, bus encryption.
use aes::cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, KeyInit};
use aes::Aes128; use aes::Aes128;
use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit, generic_array::GenericArray};
// ── AACS constants ────────────────────────────────────────────────────────── // ── AACS constants ──────────────────────────────────────────────────────────
+1 -1
View File
@@ -674,8 +674,8 @@ fn generate_host_key_pair() -> ([u8; 20], [u8; 20], [u8; 20]) {
/// AES-128-CMAC over 16 bytes of data. /// AES-128-CMAC over 16 bytes of data.
fn aes_cmac_16(data: &[u8; 16], key: &[u8; 16]) -> [u8; 16] { fn aes_cmac_16(data: &[u8; 16], key: &[u8; 16]) -> [u8; 16] {
use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
use aes::Aes128; use aes::Aes128;
use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray};
let cipher = Aes128::new(GenericArray::from_slice(key)); let cipher = Aes128::new(GenericArray::from_slice(key));
+1 -5
View File
@@ -361,11 +361,7 @@ mod tests {
/// Get KEYDB path from KEYDB_PATH environment variable. Returns None if not set or not found. /// Get KEYDB path from KEYDB_PATH environment variable. Returns None if not set or not found.
fn keydb_path() -> Option<std::path::PathBuf> { fn keydb_path() -> Option<std::path::PathBuf> {
let path = std::path::PathBuf::from(std::env::var("KEYDB_PATH").ok()?); let path = std::path::PathBuf::from(std::env::var("KEYDB_PATH").ok()?);
if path.exists() { if path.exists() { Some(path) } else { None }
Some(path)
} else {
None
}
} }
#[test] #[test]
+3 -7
View File
@@ -661,18 +661,14 @@ pub fn resolve_keys(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::decrypt::{aes_ecb_encrypt, ALIGNED_UNIT_LEN}; use super::super::decrypt::{ALIGNED_UNIT_LEN, aes_ecb_encrypt};
use super::super::keydb::{DiscEntry, KeyDb}; use super::super::keydb::{DiscEntry, KeyDb};
use super::*; use super::*;
/// Get KEYDB path from KEYDB_PATH environment variable. Returns None if not set or not found. /// Get KEYDB path from KEYDB_PATH environment variable. Returns None if not set or not found.
fn keydb_path() -> Option<std::path::PathBuf> { fn keydb_path() -> Option<std::path::PathBuf> {
let path = std::path::PathBuf::from(std::env::var("KEYDB_PATH").ok()?); let path = std::path::PathBuf::from(std::env::var("KEYDB_PATH").ok()?);
if path.exists() { if path.exists() { Some(path) } else { None }
Some(path)
} else {
None
}
} }
#[test] #[test]
@@ -849,7 +845,7 @@ mod tests {
data[23] = 1; // top_menu = CPS unit 1 data[23] = 1; // top_menu = CPS unit 1
data[24] = 0; data[24] = 0;
data[25] = 1; // num_titles = 1 data[25] = 1; // num_titles = 1
// Title 0 entry: 2 bytes pad + CPS unit // Title 0 entry: 2 bytes pad + CPS unit
data[28] = 0; data[28] = 0;
data[29] = 1; // CPS unit 1 data[29] = 1; // CPS unit 1
+5 -5
View File
@@ -21,12 +21,12 @@ pub mod keys;
// Explicit re-exports — only items needed by external consumers and sibling crate modules. // Explicit re-exports — only items needed by external consumers and sibling crate modules.
// AES primitives (aes_ecb_encrypt, aes_ecb_decrypt, aes_cbc_decrypt) are pub(crate) in decrypt.rs. // AES primitives (aes_ecb_encrypt, aes_ecb_decrypt, aes_cbc_decrypt) are pub(crate) in decrypt.rs.
pub use decrypt::{ pub use decrypt::{
decrypt_bus, decrypt_unit, decrypt_unit_full, decrypt_unit_try_keys, is_unit_encrypted, ALIGNED_UNIT_LEN, decrypt_bus, decrypt_unit, decrypt_unit_full, decrypt_unit_try_keys,
ALIGNED_UNIT_LEN, is_unit_encrypted,
}; };
pub use keydb::{DeviceKey, DiscEntry, HostCert, KeyDb}; pub use keydb::{DeviceKey, DiscEntry, HostCert, KeyDb};
pub use keys::{ pub use keys::{
decrypt_unit_key, derive_media_key_from_dk, derive_media_key_from_pk, derive_vuk, disc_hash, ContentCert, ResolvedKeys, UnitKeyFile, decrypt_unit_key, derive_media_key_from_dk,
disc_hash_hex, mkb_version, parse_content_cert, parse_unit_key_ro, read_mkb_from_drive, derive_media_key_from_pk, derive_vuk, disc_hash, disc_hash_hex, mkb_version,
resolve_keys, ContentCert, ResolvedKeys, UnitKeyFile, parse_content_cert, parse_unit_key_ro, read_mkb_from_drive, resolve_keys,
}; };
+1 -1
View File
@@ -149,7 +149,7 @@ mod tests {
let key = [0x01, 0x02, 0x03, 0x04, 0x05]; let key = [0x01, 0x02, 0x03, 0x04, 0x05];
let mut sector = vec![0xAA; 2048]; let mut sector = vec![0xAA; 2048];
sector[0x14] = 0x30; // scramble flag set sector[0x14] = 0x30; // scramble flag set
// Set a sector seed // Set a sector seed
sector[0x54..0x59].copy_from_slice(&[0x11, 0x22, 0x33, 0x44, 0x55]); sector[0x54..0x59].copy_from_slice(&[0x11, 0x22, 0x33, 0x44, 0x55]);
let original = sector.clone(); let original = sector.clone();
descramble_sector(&key, &mut sector); descramble_sector(&key, &mut sector);
+31 -7
View File
@@ -170,7 +170,9 @@ impl Mapfile {
pub fn open_or_create(path: &Path, total_size: u64, version: &str) -> io::Result<Self> { pub fn open_or_create(path: &Path, total_size: u64, version: &str) -> io::Result<Self> {
match Self::load(path) { match Self::load(path) {
Ok(mf) => Ok(mf), Ok(mf) => Ok(mf),
Err(e) if e.kind() == io::ErrorKind::NotFound => Self::create(path, total_size, version), Err(e) if e.kind() == io::ErrorKind::NotFound => {
Self::create(path, total_size, version)
}
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
@@ -290,12 +292,22 @@ impl Mapfile {
{ {
let file = std::fs::File::create(&tmp)?; let file = std::fs::File::create(&tmp)?;
let mut w = std::io::BufWriter::new(file); let mut w = std::io::BufWriter::new(file);
writeln!(w, "# Rescue Logfile. Created by libfreemkv v{}", self.version)?; writeln!(
w,
"# Rescue Logfile. Created by libfreemkv v{}",
self.version
)?;
writeln!(w, "# Current pos / status / pass / pass_time")?; writeln!(w, "# Current pos / status / pass / pass_time")?;
writeln!(w, "0x000000000 ? 1 0")?; writeln!(w, "0x000000000 ? 1 0")?;
writeln!(w, "# pos size status")?; writeln!(w, "# pos size status")?;
for e in &self.entries { for e in &self.entries {
writeln!(w, "0x{:09x} 0x{:09x} {}", e.pos, e.size, e.status.to_char())?; writeln!(
w,
"0x{:09x} 0x{:09x} {}",
e.pos,
e.size,
e.status.to_char()
)?;
} }
w.flush()?; w.flush()?;
} }
@@ -347,9 +359,18 @@ mod tests {
mf.record(200, 100, SectorStatus::Finished).unwrap(); mf.record(200, 100, SectorStatus::Finished).unwrap();
let es = mf.entries(); let es = mf.entries();
assert_eq!(es.len(), 3); assert_eq!(es.len(), 3);
assert_eq!((es[0].pos, es[0].size, es[0].status), (0, 200, SectorStatus::NonTried)); assert_eq!(
assert_eq!((es[1].pos, es[1].size, es[1].status), (200, 100, SectorStatus::Finished)); (es[0].pos, es[0].size, es[0].status),
assert_eq!((es[2].pos, es[2].size, es[2].status), (300, 700, SectorStatus::NonTried)); (0, 200, SectorStatus::NonTried)
);
assert_eq!(
(es[1].pos, es[1].size, es[1].status),
(200, 100, SectorStatus::Finished)
);
assert_eq!(
(es[2].pos, es[2].size, es[2].status),
(300, 700, SectorStatus::NonTried)
);
let _ = std::fs::remove_file(&p); let _ = std::fs::remove_file(&p);
} }
@@ -363,7 +384,10 @@ mod tests {
// Entries: [0..100 NonTried, 100..300 Finished (merged), 300..1000 NonTried] // Entries: [0..100 NonTried, 100..300 Finished (merged), 300..1000 NonTried]
let es = mf.entries(); let es = mf.entries();
assert_eq!(es.len(), 3); assert_eq!(es.len(), 3);
assert_eq!((es[1].pos, es[1].size, es[1].status), (100, 200, SectorStatus::Finished)); assert_eq!(
(es[1].pos, es[1].size, es[1].status),
(100, 200, SectorStatus::Finished)
);
let _ = std::fs::remove_file(&p); let _ = std::fs::remove_file(&p);
} }
+25 -13
View File
@@ -1243,20 +1243,26 @@ impl Disc {
if !opts.resume { if !opts.resume {
let _ = std::fs::remove_file(&mapfile_path); let _ = std::fs::remove_file(&mapfile_path);
} }
let mut map = mapfile::Mapfile::open_or_create(&mapfile_path, total_bytes, env!("CARGO_PKG_VERSION")) let mut map =
.map_err(|e| Error::IoError { source: e })?; mapfile::Mapfile::open_or_create(&mapfile_path, total_bytes, env!("CARGO_PKG_VERSION"))
.map_err(|e| Error::IoError { source: e })?;
// ISO file: if resuming and mapfile has Finished ranges, open existing; // ISO file: if resuming and mapfile has Finished ranges, open existing;
// otherwise create fresh and pre-size to total_bytes (sparse holes for // otherwise create fresh and pre-size to total_bytes (sparse holes for
// non-tried regions). // non-tried regions).
let file = if opts.resume && std::fs::metadata(path).map(|m| m.len() > 0).unwrap_or(false) { let file = if opts.resume
&& std::fs::metadata(path)
.map(|m| m.len() > 0)
.unwrap_or(false)
{
std::fs::OpenOptions::new() std::fs::OpenOptions::new()
.write(true) .write(true)
.open(path) .open(path)
.map_err(|e| Error::IoError { source: e })? .map_err(|e| Error::IoError { source: e })?
} else { } else {
let f = std::fs::File::create(path).map_err(|e| Error::IoError { source: e })?; let f = std::fs::File::create(path).map_err(|e| Error::IoError { source: e })?;
f.set_len(total_bytes).map_err(|e| Error::IoError { source: e })?; f.set_len(total_bytes)
.map_err(|e| Error::IoError { source: e })?;
f f
}; };
@@ -1290,8 +1296,7 @@ impl Disc {
// Only process the first NonTried range per outer pass; skip_forward // Only process the first NonTried range per outer pass; skip_forward
// may turn others into NonTrimmed which we DO NOT re-enter here — // may turn others into NonTrimmed which we DO NOT re-enter here —
// Disc::patch handles those. // Disc::patch handles those.
let Some((region_pos, region_size)) = map let Some((region_pos, region_size)) = map.next_with(0, mapfile::SectorStatus::NonTried)
.next_with(0, mapfile::SectorStatus::NonTried)
else { else {
break; break;
}; };
@@ -1319,8 +1324,10 @@ impl Disc {
if opts.decrypt { if opts.decrypt {
crate::decrypt::decrypt_sectors(&mut buf[..bytes], &keys, 0)?; crate::decrypt::decrypt_sectors(&mut buf[..bytes], &keys, 0)?;
} }
file.seek(SeekFrom::Start(pos)).map_err(|e| Error::IoError { source: e })?; file.seek(SeekFrom::Start(pos))
file.write_all(&buf[..bytes]).map_err(|e| Error::IoError { source: e })?; .map_err(|e| Error::IoError { source: e })?;
file.write_all(&buf[..bytes])
.map_err(|e| Error::IoError { source: e })?;
map.record(pos, block_bytes, mapfile::SectorStatus::Finished) map.record(pos, block_bytes, mapfile::SectorStatus::Finished)
.map_err(|e| Error::IoError { source: e })?; .map_err(|e| Error::IoError { source: e })?;
bytes_done = bytes_done.saturating_add(block_bytes); bytes_done = bytes_done.saturating_add(block_bytes);
@@ -1329,8 +1336,10 @@ impl Disc {
} else if opts.skip_on_error { } else if opts.skip_on_error {
// Zero-fill this block, mark non-trimmed for later patch trim. // Zero-fill this block, mark non-trimmed for later patch trim.
buf[..bytes].fill(0); buf[..bytes].fill(0);
file.seek(SeekFrom::Start(pos)).map_err(|e| Error::IoError { source: e })?; file.seek(SeekFrom::Start(pos))
file.write_all(&buf[..bytes]).map_err(|e| Error::IoError { source: e })?; .map_err(|e| Error::IoError { source: e })?;
file.write_all(&buf[..bytes])
.map_err(|e| Error::IoError { source: e })?;
map.record(pos, block_bytes, mapfile::SectorStatus::NonTrimmed) map.record(pos, block_bytes, mapfile::SectorStatus::NonTrimmed)
.map_err(|e| Error::IoError { source: e })?; .map_err(|e| Error::IoError { source: e })?;
pos += block_bytes; pos += block_bytes;
@@ -1453,7 +1462,8 @@ impl Disc {
use std::io::{Seek, SeekFrom, Write}; use std::io::{Seek, SeekFrom, Write};
let mapfile_path = mapfile_path_for(path); let mapfile_path = mapfile_path_for(path);
let mut map = mapfile::Mapfile::load(&mapfile_path).map_err(|e| Error::IoError { source: e })?; let mut map =
mapfile::Mapfile::load(&mapfile_path).map_err(|e| Error::IoError { source: e })?;
let total_bytes = map.total_size(); let total_bytes = map.total_size();
let keys = if opts.decrypt { let keys = if opts.decrypt {
self.decrypt_keys() self.decrypt_keys()
@@ -1507,8 +1517,10 @@ impl Disc {
if opts.decrypt { if opts.decrypt {
crate::decrypt::decrypt_sectors(&mut buf[..bytes], &keys, 0)?; crate::decrypt::decrypt_sectors(&mut buf[..bytes], &keys, 0)?;
} }
file.seek(SeekFrom::Start(pos)).map_err(|e| Error::IoError { source: e })?; file.seek(SeekFrom::Start(pos))
file.write_all(&buf[..bytes]).map_err(|e| Error::IoError { source: e })?; .map_err(|e| Error::IoError { source: e })?;
file.write_all(&buf[..bytes])
.map_err(|e| Error::IoError { source: e })?;
map.record(pos, block_bytes, mapfile::SectorStatus::Finished) map.record(pos, block_bytes, mapfile::SectorStatus::Finished)
.map_err(|e| Error::IoError { source: e })?; .map_err(|e| Error::IoError { source: e })?;
} else { } else {
+2 -2
View File
@@ -18,14 +18,14 @@ mod windows;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::event::{Event, EventKind}; use crate::event::{Event, EventKind};
use crate::identity::DriveId; use crate::identity::DriveId;
use crate::platform::mt1959::Mt1959;
use crate::platform::PlatformDriver; use crate::platform::PlatformDriver;
use crate::platform::mt1959::Mt1959;
use crate::profile::{self, DriveProfile}; use crate::profile::{self, DriveProfile};
use crate::scsi::ScsiTransport; use crate::scsi::ScsiTransport;
use crate::sector::SectorReader; use crate::sector::SectorReader;
use std::path::Path; use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
/// Physical state of the drive tray and disc. /// Physical state of the drive tray and disc.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
+3 -3
View File
@@ -734,12 +734,12 @@ mod tests {
let mut pgc = vec![0u8; 0xEA]; let mut pgc = vec![0u8; 0xEA];
pgc[0x02] = 1; // 1 program pgc[0x02] = 1; // 1 program
pgc[0x03] = 2; // 2 cells pgc[0x03] = 2; // 2 cells
// 1h 59m 30s at 29.97fps, 0 frames // 1h 59m 30s at 29.97fps, 0 frames
pgc[0x04] = 0x01; // hours BCD pgc[0x04] = 0x01; // hours BCD
pgc[0x05] = 0x59; // minutes BCD pgc[0x05] = 0x59; // minutes BCD
pgc[0x06] = 0x30; // seconds BCD pgc[0x06] = 0x30; // seconds BCD
pgc[0x07] = 0b11_000000; // 29.97fps, 0 frames pgc[0x07] = 0b11_000000; // 29.97fps, 0 frames
// Cell playback info offset at PGC+0xE8 // Cell playback info offset at PGC+0xE8
let cell_offset: u16 = 0xEA; // right after minimum header let cell_offset: u16 = 0xEA; // right after minimum header
pgc[0xE8] = (cell_offset >> 8) as u8; pgc[0xE8] = (cell_offset >> 8) as u8;
pgc[0xE9] = cell_offset as u8; pgc[0xE9] = cell_offset as u8;
@@ -755,7 +755,7 @@ mod tests {
pgc[co + 21] = 0; pgc[co + 21] = 0;
pgc[co + 22] = 0; pgc[co + 22] = 0;
pgc[co + 23] = 200; // last sector pgc[co + 23] = 200; // last sector
// Cell 1: sectors 300-400 // Cell 1: sectors 300-400
let co = 0xEA + 24; let co = 0xEA + 24;
pgc[co + 8] = 0; pgc[co + 8] = 0;
pgc[co + 9] = 0; pgc[co + 9] = 0;
+1 -1
View File
@@ -4,7 +4,7 @@
//! When both exist, language_streams.txt provides structured types while //! When both exist, language_streams.txt provides structured types while
//! menu_base.prop provides stream number → button name mapping. //! menu_base.prop provides stream number → button name mapping.
use super::{vocab, LabelPurpose, LabelQualifier, StreamLabel, StreamLabelType}; use super::{LabelPurpose, LabelQualifier, StreamLabel, StreamLabelType, vocab};
use crate::sector::SectorReader; use crate::sector::SectorReader;
use crate::udf::UdfFs; use crate::udf::UdfFs;
use std::collections::HashMap; use std::collections::HashMap;
+1 -1
View File
@@ -5,7 +5,7 @@
//! //!
//! Token format: `{lang}_{codec?}_{purpose?}_{region?}_` //! Token format: `{lang}_{codec?}_{purpose?}_{region?}_`
use super::{vocab, LabelPurpose, LabelQualifier, StreamLabel, StreamLabelType}; use super::{LabelPurpose, LabelQualifier, StreamLabel, StreamLabelType, vocab};
use crate::sector::SectorReader; use crate::sector::SectorReader;
use crate::udf::UdfFs; use crate::udf::UdfFs;
+5 -5
View File
@@ -95,15 +95,15 @@ pub(crate) mod udf;
pub mod verify; pub mod verify;
pub use drive::capture::{ pub use drive::capture::{
capture_drive_data, mask_bytes, mask_string, CapturedFeature, DriveCapture, CapturedFeature, DriveCapture, capture_drive_data, mask_bytes, mask_string,
}; };
pub use drive::{find_drive, find_drives, Drive, DriveStatus}; pub use drive::{Drive, DriveStatus, find_drive, find_drives};
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use event::{Event, EventKind}; pub use event::{Event, EventKind};
pub use identity::DriveId; pub use identity::DriveId;
pub use profile::DriveProfile; pub use profile::DriveProfile;
// Platform trait is pub(crate) -- callers use Drive, not Platform directly // Platform trait is pub(crate) -- callers use Drive, not Platform directly
pub use decrypt::{decrypt_sectors, DecryptKeys}; pub use decrypt::{DecryptKeys, decrypt_sectors};
pub use disc::{ pub use disc::{
AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat, Disc, AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat, Disc,
DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, Resolution, SampleRate, DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, Resolution, SampleRate,
@@ -115,8 +115,8 @@ pub use mux::MkvStream;
pub use mux::NetworkStream; pub use mux::NetworkStream;
pub use mux::NullStream; pub use mux::NullStream;
pub use mux::StdioStream; pub use mux::StdioStream;
pub use mux::{input, output, parse_url, InputOptions, StreamUrl}; pub use mux::{InputOptions, StreamUrl, input, output, parse_url};
pub use scsi::ScsiTransport; pub use scsi::ScsiTransport;
pub use sector::{FileSectorReader, SectorReader}; pub use sector::{FileSectorReader, SectorReader};
pub use speed::DriveSpeed; pub use speed::DriveSpeed;
pub use udf::{read_filesystem, UdfFs}; pub use udf::{UdfFs, read_filesystem};
+1 -1
View File
@@ -4,7 +4,7 @@
//! Buffers across PES boundaries so frames that span two PES packets //! Buffers across PES boundaries so frames that span two PES packets
//! are emitted complete, not truncated. //! are emitted complete, not truncated.
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
pub struct Ac3Parser { pub struct Ac3Parser {
/// Leftover bytes from previous PES (incomplete frame at end). /// Leftover bytes from previous PES (incomplete frame at end).
+1 -1
View File
@@ -5,7 +5,7 @@
//! Buffers across PES boundaries so frames spanning two PES packets //! Buffers across PES boundaries so frames spanning two PES packets
//! are emitted complete. //! are emitted complete.
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
const DTS_CORE_SYNC: [u8; 4] = [0x7F, 0xFE, 0x80, 0x01]; const DTS_CORE_SYNC: [u8; 4] = [0x7F, 0xFE, 0x80, 0x01];
const DTS_HD_EXT_SYNC: [u8; 4] = [0x64, 0x58, 0x20, 0x25]; const DTS_HD_EXT_SYNC: [u8; 4] = [0x64, 0x58, 0x20, 0x25];
+1 -1
View File
@@ -7,7 +7,7 @@
//! For MKV: codec ID "S_VOBSUB". //! For MKV: codec ID "S_VOBSUB".
//! All frames are keyframes (each is a complete bitmap). //! All frames are keyframes (each is a complete bitmap).
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
pub struct DvdSubParser { pub struct DvdSubParser {
/// Pre-formatted VobSub .idx palette header for codec_private. /// Pre-formatted VobSub .idx palette header for codec_private.
+2 -2
View File
@@ -4,7 +4,7 @@
//! Detects keyframes (IDR slices). //! Detects keyframes (IDR slices).
//! Each PES packet = one access unit = one frame. //! Each PES packet = one access unit = one frame.
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
/// H.264 NAL unit types we care about. /// H.264 NAL unit types we care about.
const NAL_SLICE_IDR: u8 = 5; const NAL_SLICE_IDR: u8 = 5;
@@ -263,7 +263,7 @@ mod tests {
data.extend_from_slice(&[0x00, 0x00, 0x01]); data.extend_from_slice(&[0x00, 0x00, 0x01]);
data.push(0x67); // SPS data.push(0x67); // SPS
data.extend_from_slice(&[0x42, 0x00, 0x1E, 0xAB, 0xCD]); // profile=0x42, compat=0x00, level=0x1E data.extend_from_slice(&[0x42, 0x00, 0x1E, 0xAB, 0xCD]); // profile=0x42, compat=0x00, level=0x1E
// PPS: 00 00 01 [68 <payload>] // PPS: 00 00 01 [68 <payload>]
data.extend_from_slice(&[0x00, 0x00, 0x01]); data.extend_from_slice(&[0x00, 0x00, 0x01]);
data.push(0x68); // PPS data.push(0x68); // PPS
data.extend_from_slice(&[0xCE, 0x01]); data.extend_from_slice(&[0xCE, 0x01]);
+4 -4
View File
@@ -5,7 +5,7 @@
//! Each PES packet = one access unit = one frame. //! Each PES packet = one access unit = one frame.
use super::h264::{find_start_code, skip_start_code}; use super::h264::{find_start_code, skip_start_code};
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
// HEVC NAL unit types // HEVC NAL unit types
const NAL_VPS: u8 = 32; const NAL_VPS: u8 = 32;
@@ -122,7 +122,7 @@ impl CodecParser for HevcParser {
// Minimal HEVCDecoderConfigurationRecord header // Minimal HEVCDecoderConfigurationRecord header
record.push(1); // configurationVersion record.push(1); // configurationVersion
// General profile space, tier flag, profile IDC from SPS // General profile space, tier flag, profile IDC from SPS
if sps.len() > 3 { if sps.len() > 3 {
record.push(sps[1]); // general_profile_space + general_tier_flag + general_profile_idc record.push(sps[1]); // general_profile_space + general_tier_flag + general_profile_idc
} else { } else {
@@ -154,7 +154,7 @@ impl CodecParser for HevcParser {
record.push(0xFC); record.push(0xFC);
// chromaFormat (6 + 2 bits) // chromaFormat (6 + 2 bits)
record.push(0xFC | 1); // 4:2:0 record.push(0xFC | 1); // 4:2:0
// bitDepthLumaMinus8 (5 + 3 bits) // bitDepthLumaMinus8 (5 + 3 bits)
record.push(0xF8); record.push(0xF8);
// bitDepthChromaMinus8 (5 + 3 bits) // bitDepthChromaMinus8 (5 + 3 bits)
record.push(0xF8); record.push(0xF8);
@@ -162,7 +162,7 @@ impl CodecParser for HevcParser {
record.extend_from_slice(&[0, 0]); record.extend_from_slice(&[0, 0]);
// constantFrameRate + numTemporalLayers + temporalIdNested + lengthSizeMinusOne // constantFrameRate + numTemporalLayers + temporalIdNested + lengthSizeMinusOne
record.push(0x03); // lengthSizeMinusOne = 3 (4 bytes) record.push(0x03); // lengthSizeMinusOne = 3 (4 bytes)
// numOfArrays // numOfArrays
record.push(3); // VPS, SPS, PPS record.push(3); // VPS, SPS, PPS
// VPS array // VPS array
+1 -1
View File
@@ -13,7 +13,7 @@
//! For MKV: codec ID "A_PCM/INT/BIG" (BD) or "A_PCM/INT/LIT" (DVD). //! For MKV: codec ID "A_PCM/INT/BIG" (BD) or "A_PCM/INT/LIT" (DVD).
//! All frames are keyframes (uncompressed audio). //! All frames are keyframes (uncompressed audio).
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
/// BD LPCM header size in bytes. /// BD LPCM header size in bytes.
const BD_LPCM_HEADER_SIZE: usize = 4; const BD_LPCM_HEADER_SIZE: usize = 4;
+2 -2
View File
@@ -9,7 +9,7 @@
//! - Sequence extension: 00 00 01 B5 //! - Sequence extension: 00 00 01 B5
//! - Picture header: 00 00 01 00 //! - Picture header: 00 00 01 00
use super::{pts_to_ns, CodecParser, Frame}; use super::{CodecParser, Frame, pts_to_ns};
use crate::mux::ts::PesPacket; use crate::mux::ts::PesPacket;
/// Sequence header start code suffix. /// Sequence header start code suffix.
@@ -415,7 +415,7 @@ mod tests {
// Sequence extension: 00 00 01 B5 [ext data] // Sequence extension: 00 00 01 B5 [ext data]
data.extend_from_slice(&[0x00, 0x00, 0x01, SEQ_EXT_CODE]); data.extend_from_slice(&[0x00, 0x00, 0x01, SEQ_EXT_CODE]);
data.extend_from_slice(&[0x14, 0x8A, 0x00, 0x01, 0x00, 0x00]); // ext payload data.extend_from_slice(&[0x14, 0x8A, 0x00, 0x01, 0x00, 0x00]); // ext payload
// Picture header follows. // Picture header follows.
data.extend_from_slice(&make_picture_header(PICTURE_TYPE_I)); data.extend_from_slice(&make_picture_header(PICTURE_TYPE_I));
data.extend_from_slice(&[0xFF; 4]); data.extend_from_slice(&[0xFF; 4]);
+1 -1
View File
@@ -4,7 +4,7 @@
//! Each PES packet contains one or more segments. //! Each PES packet contains one or more segments.
//! All segments are keyframes (no inter-segment dependencies). //! All segments are keyframes (no inter-segment dependencies).
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
pub struct PgsParser; pub struct PgsParser;
+1 -1
View File
@@ -11,7 +11,7 @@
//! AC-3 frames (interleaved, same PID): start with sync word 0x0B77. //! AC-3 frames (interleaved, same PID): start with sync word 0x0B77.
//! We skip AC-3 frames and only emit TrueHD access units. //! We skip AC-3 frames and only emit TrueHD access units.
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
/// Duration of one TrueHD access unit in nanoseconds (1/1200 second). /// Duration of one TrueHD access unit in nanoseconds (1/1200 second).
const AU_DURATION_NS: i64 = 833_333; const AU_DURATION_NS: i64 = 833_333;
+1 -1
View File
@@ -5,7 +5,7 @@
//! Frame start = Frame header start code (0x0D). //! Frame start = Frame header start code (0x0D).
//! I-frames (keyframes) are identified from the frame header. //! I-frames (keyframes) are identified from the frame header.
use super::{pts_to_ns, CodecParser, Frame, PesPacket}; use super::{CodecParser, Frame, PesPacket, pts_to_ns};
const SC_SEQUENCE_HEADER: u8 = 0x0F; const SC_SEQUENCE_HEADER: u8 = 0x0F;
const SC_ENTRY_POINT: u8 = 0x0E; const SC_ENTRY_POINT: u8 = 0x0E;
+3 -11
View File
@@ -9,8 +9,8 @@ use crate::disc::{Disc, DiscTitle, Extent};
use crate::event::{BatchSizeReason, Event, EventKind}; use crate::event::{BatchSizeReason, Event, EventKind};
use crate::sector::SectorReader; use crate::sector::SectorReader;
use std::io; use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
/// Ramp back up to the preferred batch size after this many sectors /// Ramp back up to the preferred batch size after this many sectors
/// of clean reading at the current (reduced) size. 100 MiB = 51,200 sectors. /// of clean reading at the current (reduced) size. 100 MiB = 51,200 sectors.
@@ -26,22 +26,14 @@ const PROBE_THRESHOLD_SECTORS: u32 = 100 * 1024 * 1024 / 2048;
/// through 3 → 1 without intermediate unaligned sizes. /// through 3 → 1 without intermediate unaligned sizes.
fn halve_batch_size(size: u16) -> u16 { fn halve_batch_size(size: u16) -> u16 {
let h = (size / 2).max(1); let h = (size / 2).max(1);
if h >= 6 { if h >= 6 { h - (h % 3) } else { h }
h - (h % 3)
} else {
h
}
} }
/// Double a batch size toward a preferred max, keeping 3-sector alignment /// Double a batch size toward a preferred max, keeping 3-sector alignment
/// when the result is >= 6. /// when the result is >= 6.
fn double_batch_size(size: u16, preferred: u16) -> u16 { fn double_batch_size(size: u16, preferred: u16) -> u16 {
let d = size.saturating_mul(2).min(preferred); let d = size.saturating_mul(2).min(preferred);
if d >= 6 { if d >= 6 { d - (d % 3) } else { d }
d - (d % 3)
} else {
d
}
} }
/// Adaptive batch sizer. Shrinks on read failure, grows after a sustained /// Adaptive batch sizer. Shrinks on read failure, grows after a sustained
+1 -1
View File
@@ -441,7 +441,7 @@ mod tests {
fn test_write_uint() { fn test_write_uint() {
let mut buf = Vec::new(); let mut buf = Vec::new();
write_uint(&mut buf, 0x4286, 1).unwrap(); // EBML_VERSION = 1 write_uint(&mut buf, 0x4286, 1).unwrap(); // EBML_VERSION = 1
// ID: 42 86, Size: 81 (1 byte), Data: 01 // ID: 42 86, Size: 81 (1 byte), Data: 01
assert_eq!(buf, [0x42, 0x86, 0x81, 0x01]); assert_eq!(buf, [0x42, 0x86, 0x81, 0x01]);
} }
+1 -1
View File
@@ -699,7 +699,7 @@ mod tests {
// Track VINT: 1 byte (track 1 = 0x81) // Track VINT: 1 byte (track 1 = 0x81)
let track_vint_pos = after_id + size_len; let track_vint_pos = after_id + size_len;
let track_vint_len = 1; // track 1 encoded as 0x81 let track_vint_len = 1; // track 1 encoded as 0x81
// 2-byte relative timestamp // 2-byte relative timestamp
let ts_pos = track_vint_pos + track_vint_len; let ts_pos = track_vint_pos + track_vint_len;
// flags byte // flags byte
let flags_pos = ts_pos + 2; let flags_pos = ts_pos + 2;
+1 -1
View File
@@ -4,7 +4,7 @@
//! Write: PES frames in → MKV mux → Matroska container. //! Write: PES frames in → MKV mux → Matroska container.
use super::mkv::{MkvMuxer, MkvTrack}; use super::mkv::{MkvMuxer, MkvTrack};
use super::{ebml, WriteSeek}; use super::{WriteSeek, ebml};
type MkvHeaderResult = io::Result<(crate::disc::DiscTitle, Vec<(u16, Vec<u8>)>)>; type MkvHeaderResult = io::Result<(crate::disc::DiscTitle, Vec<(u16, Vec<u8>)>)>;
+1 -1
View File
@@ -38,7 +38,7 @@ pub use m2ts::M2tsStream;
pub use mkvstream::MkvStream; pub use mkvstream::MkvStream;
pub use network::NetworkStream; pub use network::NetworkStream;
pub use null::NullStream; pub use null::NullStream;
pub use resolve::{input, output, parse_url, InputOptions, StreamUrl}; pub use resolve::{InputOptions, StreamUrl, input, output, parse_url};
pub use stdio::StdioStream; pub use stdio::StdioStream;
use std::io::{Seek, Write}; use std::io::{Seek, Write};
+1 -5
View File
@@ -227,11 +227,7 @@ fn hvcc_to_annex_b(hvcc: &[u8]) -> Option<Vec<u8>> {
} }
} }
if out.is_empty() { if out.is_empty() { None } else { Some(out) }
None
} else {
Some(out)
}
} }
/// Convert length-prefixed NALUs (4-byte BE length + NAL) to Annex B /// Convert length-prefixed NALUs (4-byte BE length + NAL) to Annex B
+3 -3
View File
@@ -101,7 +101,7 @@ impl UdfFs {
None => { None => {
return Err(Error::UdfNotFound { return Err(Error::UdfNotFound {
path: path.to_string(), path: path.to_string(),
}) });
} }
}; };
let entry = current let entry = current
@@ -136,7 +136,7 @@ impl UdfFs {
None => { None => {
return Err(Error::UdfNotFound { return Err(Error::UdfNotFound {
path: path.to_string(), path: path.to_string(),
}) });
} }
}; };
let entry = current let entry = current
@@ -375,7 +375,7 @@ impl UdfFs {
None => { None => {
return Err(Error::UdfNotFound { return Err(Error::UdfNotFound {
path: path.to_string(), path: path.to_string(),
}) });
} }
}; };
let entry = current let entry = current
+5 -5
View File
@@ -73,8 +73,8 @@ fn css_is_scrambled_detection() {
/// then decrypt with decrypt_unit() and verify the plaintext matches. /// then decrypt with decrypt_unit() and verify the plaintext matches.
#[test] #[test]
fn aacs_decrypt_unit_roundtrip() { fn aacs_decrypt_unit_roundtrip() {
use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
use aes::Aes128; use aes::Aes128;
use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray};
let unit_key = [0xAAu8; 16]; let unit_key = [0xAAu8; 16];
let aacs_iv: [u8; 16] = [ let aacs_iv: [u8; 16] = [
@@ -207,8 +207,8 @@ fn aacs_disc_hash_deterministic() {
/// decrypt_unit_key recovers the original. /// decrypt_unit_key recovers the original.
#[test] #[test]
fn aacs_decrypt_unit_key_roundtrip() { fn aacs_decrypt_unit_key_roundtrip() {
use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
use aes::Aes128; use aes::Aes128;
use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray};
let vuk = [ let vuk = [
0x11u8, 0x14, 0x36, 0x0B, 0x10, 0xEE, 0x6E, 0xAC, 0x78, 0xAA, 0x4A, 0xC0, 0xB7, 0x52, 0xEA, 0x11u8, 0x14, 0x36, 0x0B, 0x10, 0xEE, 0x6E, 0xAC, 0x78, 0xAA, 0x4A, 0xC0, 0xB7, 0x52, 0xEA,
@@ -309,8 +309,8 @@ fn aacs_decrypt_unit_unencrypted_passthrough() {
/// Independent AES-128-ECB encrypt (uses `aes` crate directly, NOT our library). /// Independent AES-128-ECB encrypt (uses `aes` crate directly, NOT our library).
fn ref_aes_ecb_encrypt(key: &[u8; 16], data: &[u8; 16]) -> [u8; 16] { fn ref_aes_ecb_encrypt(key: &[u8; 16], data: &[u8; 16]) -> [u8; 16] {
use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
use aes::Aes128; use aes::Aes128;
use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray};
let cipher = Aes128::new(GenericArray::from_slice(key)); let cipher = Aes128::new(GenericArray::from_slice(key));
let mut block = GenericArray::clone_from_slice(data); let mut block = GenericArray::clone_from_slice(data);
cipher.encrypt_block(&mut block); cipher.encrypt_block(&mut block);
@@ -321,8 +321,8 @@ fn ref_aes_ecb_encrypt(key: &[u8; 16], data: &[u8; 16]) -> [u8; 16] {
/// Independent AES-128-CBC encrypt (uses `aes` crate directly, NOT our library). /// Independent AES-128-CBC encrypt (uses `aes` crate directly, NOT our library).
fn ref_aes_cbc_encrypt(key: &[u8; 16], iv: &[u8; 16], data: &mut [u8]) { fn ref_aes_cbc_encrypt(key: &[u8; 16], iv: &[u8; 16], data: &mut [u8]) {
use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
use aes::Aes128; use aes::Aes128;
use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray};
let cipher = Aes128::new(GenericArray::from_slice(key)); let cipher = Aes128::new(GenericArray::from_slice(key));
let mut prev = *iv; let mut prev = *iv;
let num_blocks = data.len() / 16; let num_blocks = data.len() / 16;
@@ -703,7 +703,7 @@ fn aacs_parse_unit_key_ro_minimal() {
data[0..4].copy_from_slice(&uk_pos.to_be_bytes()); data[0..4].copy_from_slice(&uk_pos.to_be_bytes());
// app_type // app_type
data[16] = 1; // BD-ROM data[16] = 1; // BD-ROM
// num_bdmv_dir // num_bdmv_dir
data[17] = 1; data[17] = 1;
// flags // flags
data[18] = 0; data[18] = 0;
+8 -2
View File
@@ -1,7 +1,7 @@
//! Disc scanning pipeline tests. //! Disc scanning pipeline tests.
use libfreemkv::error::Result;
use libfreemkv::SectorReader; use libfreemkv::SectorReader;
use libfreemkv::error::Result;
use libfreemkv::{Disc, DiscTitle, ScanOptions}; use libfreemkv::{Disc, DiscTitle, ScanOptions};
use std::collections::HashMap; use std::collections::HashMap;
@@ -21,7 +21,13 @@ impl MockSectorReader {
} }
impl SectorReader for MockSectorReader { impl SectorReader for MockSectorReader {
fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8], _recovery: bool) -> Result<usize> { fn read_sectors(
&mut self,
lba: u32,
count: u16,
buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
let total = count as usize * SECTOR_SIZE; let total = count as usize * SECTOR_SIZE;
for i in 0..count as u32 { for i in 0..count as u32 {
let offset = i as usize * SECTOR_SIZE; let offset = i as usize * SECTOR_SIZE;
+8 -2
View File
@@ -1,7 +1,7 @@
//! UDF parser tests using a MockSectorReader. //! UDF parser tests using a MockSectorReader.
use libfreemkv::error::Result; use libfreemkv::error::Result;
use libfreemkv::{read_filesystem, SectorReader}; use libfreemkv::{SectorReader, read_filesystem};
use std::collections::HashMap; use std::collections::HashMap;
const SECTOR_SIZE: usize = 2048; const SECTOR_SIZE: usize = 2048;
@@ -39,7 +39,13 @@ impl MockSectorReader {
} }
impl SectorReader for MockSectorReader { impl SectorReader for MockSectorReader {
fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8], _recovery: bool) -> Result<usize> { fn read_sectors(
&mut self,
lba: u32,
count: u16,
buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
let total = count as usize * SECTOR_SIZE; let total = count as usize * SECTOR_SIZE;
assert!(buf.len() >= total, "buffer too small"); assert!(buf.len() >= total, "buffer too small");
for i in 0..count as u32 { for i in 0..count as u32 {