style: cargo fmt
This commit is contained in:
+1
-1
@@ -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 ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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};
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user