v0.11.15: lint cleanup — fmt + clippy clean
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 0.11.15 (2026-04-21)
|
||||
|
||||
### Lint cleanup
|
||||
- Fix all `cargo fmt` and `cargo clippy -D warnings` across codebase.
|
||||
- Remove unused imports (Codec, HdrFormat, ScanOptions, detect_max_batch_sectors, Extent).
|
||||
- Fix CSS tuple pattern deref, collapsible if-statement, div_ceil reimplementation.
|
||||
|
||||
## 0.11.14 (2026-04-21)
|
||||
|
||||
### Audit fixes: read recovery, verify, SCSI
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libfreemkv"
|
||||
version = "0.11.14"
|
||||
version = "0.11.15"
|
||||
edition = "2021"
|
||||
rust-version = "1.86"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
+2
-1
@@ -348,7 +348,7 @@ fn read_disc_key(drive: &mut Drive, agid: u8, bus_key: &[u8; 5]) -> Result<[u8;
|
||||
let candidate = super::lfsr::decrypt_key(0x00, player_key, &enc);
|
||||
|
||||
// Check if any previous candidate matches (same disc key from different entry/pk)
|
||||
for &(ref prev, _, _) in &candidates {
|
||||
for (prev, _, _) in &candidates {
|
||||
if *prev == candidate {
|
||||
return Ok(candidate);
|
||||
}
|
||||
@@ -391,6 +391,7 @@ fn read_raw_title_key(drive: &mut Drive, agid: u8, lba: u32) -> Result<[u8; 5]>
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn read_title_key(
|
||||
drive: &mut Drive,
|
||||
agid: u8,
|
||||
|
||||
+4
-6
@@ -938,9 +938,7 @@ pub struct DiscId {
|
||||
impl DiscId {
|
||||
/// Best available name: meta_title, then formatted volume_id.
|
||||
pub fn name(&self) -> &str {
|
||||
self.meta_title
|
||||
.as_deref()
|
||||
.unwrap_or(&self.volume_id)
|
||||
self.meta_title.as_deref().unwrap_or(&self.volume_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,11 +1029,11 @@ impl Disc {
|
||||
{
|
||||
let lba = disc.titles[0].extents.iter().find_map(|ext| {
|
||||
let mut buf = vec![0u8; 2048];
|
||||
if session.read_sectors(ext.start_lba, 1, &mut buf).is_ok() {
|
||||
if crate::css::is_scrambled(&buf) {
|
||||
if session.read_sectors(ext.start_lba, 1, &mut buf).is_ok()
|
||||
&& crate::css::is_scrambled(&buf)
|
||||
{
|
||||
return Some(ext.start_lba);
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
|
||||
+7
-1
@@ -714,7 +714,13 @@ impl SectorReader for Drive {
|
||||
self.read(lba, count, buf, true)
|
||||
}
|
||||
|
||||
fn read_sectors_recover(&mut self, lba: u32, count: u16, buf: &mut [u8], recovery: bool) -> Result<usize> {
|
||||
fn read_sectors_recover(
|
||||
&mut self,
|
||||
lba: u32,
|
||||
count: u16,
|
||||
buf: &mut [u8],
|
||||
recovery: bool,
|
||||
) -> Result<usize> {
|
||||
self.read(lba, count, buf, recovery)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-10
@@ -81,20 +81,13 @@ pub enum EventKind {
|
||||
},
|
||||
|
||||
/// Binary search isolated and recovered a marginal sector.
|
||||
SectorRecovered {
|
||||
sector: u64,
|
||||
},
|
||||
SectorRecovered { sector: u64 },
|
||||
|
||||
/// Sector unreadable, zero-filled (skip mode).
|
||||
SectorSkipped {
|
||||
sector: u64,
|
||||
},
|
||||
SectorSkipped { sector: u64 },
|
||||
|
||||
/// Binary search activated — batch failed, isolating bad sector.
|
||||
BinarySearch {
|
||||
sector: u64,
|
||||
batch_size: u16,
|
||||
},
|
||||
BinarySearch { sector: u64, batch_size: u16 },
|
||||
|
||||
/// Operation complete.
|
||||
Complete {
|
||||
|
||||
+3
-1
@@ -564,7 +564,9 @@ fn parse_pgc(data: &[u8], pgc_offset: usize, chapters: u16) -> Result<DvdTitle>
|
||||
}
|
||||
// Program map: each byte is the first cell number (1-based) for that program
|
||||
for p in 0..nr_of_programs {
|
||||
if pgm_base + p >= data.len() { break; }
|
||||
if pgm_base + p >= data.len() {
|
||||
break;
|
||||
}
|
||||
let first_cell = data[pgm_base + p] as usize;
|
||||
// Chapter time = sum of cell durations before this program's first cell
|
||||
let time: f64 = cell_durations[..first_cell.saturating_sub(1)].iter().sum();
|
||||
|
||||
+3
-2
@@ -142,7 +142,7 @@ pub fn apply(reader: &mut dyn SectorReader, udf: &UdfFs, titles: &mut [DiscTitle
|
||||
/// Runs after BD-J label extraction — fills gaps with codec + channel descriptions.
|
||||
/// This is the central place for all fallback label generation.
|
||||
pub fn fill_defaults(titles: &mut [crate::disc::DiscTitle]) {
|
||||
use crate::disc::{Codec, HdrFormat, Stream};
|
||||
use crate::disc::Stream;
|
||||
|
||||
for title in titles.iter_mut() {
|
||||
for stream in &mut title.streams {
|
||||
@@ -151,7 +151,8 @@ pub fn fill_defaults(titles: &mut [crate::disc::DiscTitle]) {
|
||||
a.label = generate_audio_label(&a.codec, &a.channels, a.secondary);
|
||||
}
|
||||
Stream::Video(v) if v.label.is_empty() => {
|
||||
v.label = generate_video_label(&v.codec, v.resolution.pixels(), &v.hdr, v.secondary);
|
||||
v.label =
|
||||
generate_video_label(&v.codec, v.resolution.pixels(), &v.hdr, v.secondary);
|
||||
}
|
||||
Stream::Subtitle(s) if s.forced => {
|
||||
// Ensure forced subs are labeled even if BD-J didn't set a name
|
||||
|
||||
+1
-1
@@ -90,9 +90,9 @@ pub(crate) mod platform;
|
||||
pub mod profile;
|
||||
pub mod scsi;
|
||||
pub mod sector;
|
||||
pub mod verify;
|
||||
pub(crate) mod speed;
|
||||
pub(crate) mod udf;
|
||||
pub mod verify;
|
||||
|
||||
pub use drive::capture::{
|
||||
capture_drive_data, mask_bytes, mask_string, CapturedFeature, DriveCapture,
|
||||
|
||||
@@ -153,7 +153,7 @@ impl CodecParser for Mpeg2Parser {
|
||||
bits += 64 * 8;
|
||||
}
|
||||
}
|
||||
let total_bytes = 4 + ((bits + 7) / 8) as usize;
|
||||
let total_bytes = 4 + bits.div_ceil(8) as usize;
|
||||
(sc + total_bytes).min(data.len())
|
||||
}
|
||||
}
|
||||
|
||||
+9
-12
@@ -50,30 +50,27 @@ impl TrueHdParser {
|
||||
0 => {
|
||||
// 48 kHz
|
||||
static SIZES: [usize; 38] = [
|
||||
64, 64, 80, 80, 96, 96, 112, 112, 128, 128,
|
||||
160, 160, 192, 192, 224, 224, 256, 256, 320, 320,
|
||||
384, 384, 448, 448, 512, 512, 640, 640, 768, 768,
|
||||
896, 896, 1024, 1024, 1152, 1152, 1280, 1280,
|
||||
64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, 224, 224, 256,
|
||||
256, 320, 320, 384, 384, 448, 448, 512, 512, 640, 640, 768, 768, 896, 896,
|
||||
1024, 1024, 1152, 1152, 1280, 1280,
|
||||
];
|
||||
SIZES.get(frmsizecod).copied().unwrap_or(0)
|
||||
}
|
||||
1 => {
|
||||
// 44.1 kHz
|
||||
static SIZES: [usize; 38] = [
|
||||
69, 70, 87, 88, 104, 105, 121, 122, 139, 140,
|
||||
174, 175, 208, 209, 243, 244, 278, 279, 348, 349,
|
||||
417, 418, 487, 488, 557, 558, 696, 697, 835, 836,
|
||||
975, 976, 1114, 1115, 1253, 1254, 1393, 1394,
|
||||
69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, 243, 244,
|
||||
278, 279, 348, 349, 417, 418, 487, 488, 557, 558, 696, 697, 835, 836, 975, 976,
|
||||
1114, 1115, 1253, 1254, 1393, 1394,
|
||||
];
|
||||
SIZES.get(frmsizecod).copied().unwrap_or(0)
|
||||
}
|
||||
2 => {
|
||||
// 32 kHz
|
||||
static SIZES: [usize; 38] = [
|
||||
96, 96, 120, 120, 144, 144, 168, 168, 192, 192,
|
||||
240, 240, 288, 288, 336, 336, 384, 384, 480, 480,
|
||||
576, 576, 672, 672, 768, 768, 960, 960, 1152, 1152,
|
||||
1344, 1344, 1536, 1536, 1728, 1728, 1920, 1920,
|
||||
96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, 288, 336, 336,
|
||||
384, 384, 480, 480, 576, 576, 672, 672, 768, 768, 960, 960, 1152, 1152, 1344,
|
||||
1344, 1536, 1536, 1728, 1728, 1920, 1920,
|
||||
];
|
||||
SIZES.get(frmsizecod).copied().unwrap_or(0)
|
||||
}
|
||||
|
||||
+7
-2
@@ -5,7 +5,7 @@
|
||||
//!
|
||||
//! Read-only. For disc→ISO (raw sector copy), use `Disc::copy()`.
|
||||
|
||||
use crate::disc::{detect_max_batch_sectors, Disc, DiscTitle, Extent, ScanOptions};
|
||||
use crate::disc::{Disc, DiscTitle, Extent};
|
||||
use crate::event::{Event, EventKind};
|
||||
use crate::sector::SectorReader;
|
||||
use std::io;
|
||||
@@ -171,7 +171,12 @@ impl DiscStream {
|
||||
let offset = self.buf_valid;
|
||||
if self
|
||||
.reader
|
||||
.read_sectors_recover(lba, count, &mut self.read_buf[offset..offset + bytes], false)
|
||||
.read_sectors_recover(
|
||||
lba,
|
||||
count,
|
||||
&mut self.read_buf[offset..offset + bytes],
|
||||
false,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
self.buf_valid += bytes;
|
||||
|
||||
+23
-6
@@ -5,7 +5,9 @@
|
||||
//! cues and seek head are finalized at the end.
|
||||
|
||||
use super::ebml;
|
||||
use crate::disc::{AudioStream, Chapter, Codec, ColorSpace, HdrFormat, SubtitleStream, VideoStream};
|
||||
use crate::disc::{
|
||||
AudioStream, Chapter, Codec, ColorSpace, HdrFormat, SubtitleStream, VideoStream,
|
||||
};
|
||||
use std::io::{self, Seek, SeekFrom, Write};
|
||||
|
||||
/// MKV track definition (built from disc stream metadata).
|
||||
@@ -268,7 +270,11 @@ impl<W: Write + Seek> MkvMuxer<W> {
|
||||
|
||||
// DefaultDuration — frame duration in nanoseconds
|
||||
if track.default_duration_ns > 0 {
|
||||
ebml::write_uint(&mut writer, ebml::DEFAULT_DURATION, track.default_duration_ns)?;
|
||||
ebml::write_uint(
|
||||
&mut writer,
|
||||
ebml::DEFAULT_DURATION,
|
||||
track.default_duration_ns,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Video-specific
|
||||
@@ -278,13 +284,25 @@ impl<W: Write + Seek> MkvMuxer<W> {
|
||||
ebml::write_uint(&mut writer, ebml::PIXEL_HEIGHT, track.pixel_height as u64)?;
|
||||
if track.display_width > 0 && track.display_height > 0 {
|
||||
ebml::write_uint(&mut writer, ebml::DISPLAY_WIDTH, track.display_width as u64)?;
|
||||
ebml::write_uint(&mut writer, ebml::DISPLAY_HEIGHT, track.display_height as u64)?;
|
||||
ebml::write_uint(
|
||||
&mut writer,
|
||||
ebml::DISPLAY_HEIGHT,
|
||||
track.display_height as u64,
|
||||
)?;
|
||||
}
|
||||
// Colour metadata (HDR)
|
||||
if track.colour_matrix > 0 || track.colour_transfer > 0 {
|
||||
let col_pos = ebml::start_master(&mut writer, ebml::COLOUR)?;
|
||||
ebml::write_uint(&mut writer, ebml::MATRIX_COEFFICIENTS, track.colour_matrix as u64)?;
|
||||
ebml::write_uint(&mut writer, ebml::TRANSFER_CHARACTERISTICS, track.colour_transfer as u64)?;
|
||||
ebml::write_uint(
|
||||
&mut writer,
|
||||
ebml::MATRIX_COEFFICIENTS,
|
||||
track.colour_matrix as u64,
|
||||
)?;
|
||||
ebml::write_uint(
|
||||
&mut writer,
|
||||
ebml::TRANSFER_CHARACTERISTICS,
|
||||
track.colour_transfer as u64,
|
||||
)?;
|
||||
ebml::write_uint(&mut writer, ebml::PRIMARIES, track.colour_primaries as u64)?;
|
||||
ebml::write_uint(&mut writer, ebml::RANGE, track.colour_range as u64)?;
|
||||
ebml::end_master(&mut writer, col_pos)?;
|
||||
@@ -845,4 +863,3 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -246,7 +246,6 @@ impl SgIoTransport {
|
||||
|
||||
device.to_path_buf()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Drop for SgIoTransport {
|
||||
@@ -335,8 +334,8 @@ impl ScsiTransport for SgIoTransport {
|
||||
|
||||
// Wait for completion with enforceable timeout.
|
||||
// Retry on EINTR (signal interrupted poll) with remaining time.
|
||||
let deadline = std::time::Instant::now()
|
||||
+ std::time::Duration::from_millis(timeout_ms as u64);
|
||||
let deadline =
|
||||
std::time::Instant::now() + std::time::Duration::from_millis(timeout_ms as u64);
|
||||
let pr = loop {
|
||||
let remaining = deadline
|
||||
.saturating_duration_since(std::time::Instant::now())
|
||||
|
||||
+7
-1
@@ -16,7 +16,13 @@ pub trait SectorReader: Send {
|
||||
/// Read with explicit recovery flag.
|
||||
/// true = full retry/reset loop (for ripping). false = single attempt, fast fail (for verify).
|
||||
/// Default: delegates to read_sectors (recovery=true behavior).
|
||||
fn read_sectors_recover(&mut self, lba: u32, count: u16, buf: &mut [u8], recovery: bool) -> Result<usize> {
|
||||
fn read_sectors_recover(
|
||||
&mut self,
|
||||
lba: u32,
|
||||
count: u16,
|
||||
buf: &mut [u8],
|
||||
recovery: bool,
|
||||
) -> Result<usize> {
|
||||
// Default ignores flag — file-backed readers don't have recovery
|
||||
let _ = recovery;
|
||||
self.read_sectors(lba, count, buf)
|
||||
|
||||
+20
-7
@@ -1,6 +1,6 @@
|
||||
//! Disc sector verification — read every sector and classify health.
|
||||
|
||||
use crate::disc::{Chapter, DiscTitle, Extent};
|
||||
use crate::disc::{Chapter, DiscTitle};
|
||||
use crate::sector::SectorReader;
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -55,7 +55,12 @@ impl VerifyResult {
|
||||
}
|
||||
|
||||
/// Map a bad sector range to a chapter timestamp.
|
||||
pub fn chapter_at_offset(chapters: &[Chapter], byte_offset: u64, duration_secs: f64, total_bytes: u64) -> Option<(usize, f64)> {
|
||||
pub fn chapter_at_offset(
|
||||
chapters: &[Chapter],
|
||||
byte_offset: u64,
|
||||
duration_secs: f64,
|
||||
total_bytes: u64,
|
||||
) -> Option<(usize, f64)> {
|
||||
if total_bytes == 0 || chapters.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -150,7 +155,12 @@ pub fn verify_title(
|
||||
|
||||
let s1 = Instant::now();
|
||||
let first_ok = reader
|
||||
.read_sectors_recover(sector_lba, 1, &mut buf[sector_offset..sector_offset + 2048], false)
|
||||
.read_sectors_recover(
|
||||
sector_lba,
|
||||
1,
|
||||
&mut buf[sector_offset..sector_offset + 2048],
|
||||
false,
|
||||
)
|
||||
.is_ok();
|
||||
let s1_ms = s1.elapsed().as_millis();
|
||||
|
||||
@@ -164,7 +174,12 @@ pub fn verify_title(
|
||||
// Retry once more after brief pause
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
if reader
|
||||
.read_sectors_recover(sector_lba, 1, &mut buf[sector_offset..sector_offset + 2048], false)
|
||||
.read_sectors_recover(
|
||||
sector_lba,
|
||||
1,
|
||||
&mut buf[sector_offset..sector_offset + 2048],
|
||||
false,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
recovered += 1;
|
||||
@@ -178,9 +193,7 @@ pub fn verify_title(
|
||||
if status != SectorStatus::Good {
|
||||
// Merge with previous range if contiguous and same status
|
||||
if let Some(last) = ranges.last_mut() {
|
||||
if last.status == status
|
||||
&& last.start_lba + last.count == sector_lba
|
||||
{
|
||||
if last.status == status && last.start_lba + last.count == sector_lba {
|
||||
last.count += 1;
|
||||
} else {
|
||||
ranges.push(SectorRange {
|
||||
|
||||
Reference in New Issue
Block a user