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