first draft

This commit is contained in:
tux
2026-11-03 20:19:21 +01:00
commit 158e115242
44 changed files with 10785 additions and 0 deletions
+351
View File
@@ -0,0 +1,351 @@
//! Callback and progress display, ported from the callback in main.c.
use crate::log::Log;
use cdrustanoia_core::callback::*;
use cdrustanoia_core::device::CD_FRAMEWORDS;
use std::io::{self, Write};
use std::time::{SystemTime, UNIX_EPOCH};
pub const CALLBACK_STRINGS: [&str; 16] = [
"wrote",
"finished",
"read",
"verify",
"jitter",
"correction",
"scratch",
"scratch repair",
"skip",
"drift",
"backoff",
"overlap",
"dropped",
"duped",
"transport error",
"cache error",
];
const GRAPH: usize = 30;
pub struct Progress {
pub callbegin: i64,
pub callend: i64,
pub callscript: bool,
pub abort_on_skip: bool,
pub skipped_flag: bool,
c_sector: i64,
v_sector: i64,
dispcache: [u8; GRAPH],
last: i32,
lasttime: i64,
heartbeat: u8,
slevel: i32,
slast: i32,
stimeout: i32,
cacheerr: i32,
overlap: i32,
printit: i32,
}
impl Default for Progress {
fn default() -> Self {
Self {
callbegin: 0,
callend: 0,
callscript: false,
abort_on_skip: false,
skipped_flag: false,
c_sector: 0,
v_sector: 0,
dispcache: [b' '; GRAPH],
last: 0,
lasttime: 0,
heartbeat: b' ',
slevel: 0,
slast: 0,
stimeout: 0,
cacheerr: 0,
overlap: 0,
printit: -1,
}
}
}
impl Progress {
pub fn reset_batch(&mut self) {
self.c_sector = 0;
self.v_sector = 0;
self.dispcache = [b' '; GRAPH];
self.last = 0;
self.lasttime = 0;
self.heartbeat = b' ';
self.slevel = 0;
self.slast = 0;
self.stimeout = 0;
self.overlap = 0;
self.skipped_flag = false;
}
fn now_tenths() -> i64 {
match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => d.as_secs() as i64 * 10 + (d.subsec_micros() / 100_000) as i64,
Err(_) => 0,
}
}
pub fn callback(&mut self, inpos: i64, function: i32, log: &mut Log) {
if self.callscript {
let name = if (-2..=13).contains(&function) {
CALLBACK_STRINGS[(function + 2) as usize]
} else {
""
};
let _ = writeln!(io::stderr(), "##: {} [{}] @ {}", function, name, inpos);
} else if function == PARANOIA_CB_CACHEERR {
if self.cacheerr == 0 {
let _ = write!(
io::stderr(),
"\rWARNING: The CDROM drive appears to be seeking impossibly quickly.\n\
This could be due to timer bugs, a drive that really is improbably fast,\n\
or, most likely, a bug in cdparanoia's cache modelling.\n\n\
Please consider using the -A option to perform an analysis run, then mail\n\
the cdparanoia.log file produced by the analysis to paranoia-dev@xiph.org\n\
to assist developers in correcting the problem.\n\n"
);
}
self.cacheerr += 1;
}
if function == PARANOIA_CB_SKIP {
// Keep -X functional even when progress is redirected or quiet.
self.skipped_flag = true;
}
if log.quiet {
return;
}
let osector = inpos;
let sector = inpos / CD_FRAMEWORDS;
if self.printit == -1 {
self.printit = if unsafe { libc::isatty(2) } == 1 {
1
} else {
0
};
}
if self.printit == 1 {
let position = if self.callend != self.callbegin {
((sector - self.callbegin) as f64 / (self.callend - self.callbegin) as f64
* GRAPH as f64) as i32
} else {
0
};
let aheadposition = if self.callend != self.callbegin {
((self.c_sector - self.callbegin) as f64 / (self.callend - self.callbegin) as f64
* GRAPH as f64) as i32
} else {
0
};
if function == -2 {
self.v_sector = sector;
return;
}
if function == -1 {
self.last = 8;
self.heartbeat = b'*';
self.slevel = 0;
self.v_sector = sector;
} else if position < GRAPH as i32 && position >= 0 {
let p = position as usize;
match function {
PARANOIA_CB_VERIFY => {
if self.stimeout >= 30 {
if self.overlap as i64 > CD_FRAMEWORDS {
self.slevel = 2;
} else {
self.slevel = 1;
}
}
}
PARANOIA_CB_READ => {
if sector > self.c_sector {
self.c_sector = sector;
}
}
PARANOIA_CB_FIXUP_EDGE => {
if self.stimeout >= 5 {
if self.overlap as i64 > CD_FRAMEWORDS {
self.slevel = 2;
} else {
self.slevel = 1;
}
}
if self.dispcache[p] == b' ' {
self.dispcache[p] = b'-';
}
}
PARANOIA_CB_FIXUP_ATOM => {
if self.slevel < 3 || self.stimeout > 5 {
self.slevel = 3;
}
if self.dispcache[p] == b' ' || self.dispcache[p] == b'-' {
self.dispcache[p] = b'+';
}
}
PARANOIA_CB_READERR => {
self.slevel = 6;
if self.dispcache[p] != b'V' && self.dispcache[p] != b'C' {
self.dispcache[p] = b'e';
}
}
PARANOIA_CB_CACHEERR => {
self.slevel = 8;
self.dispcache[p] = b'C';
}
PARANOIA_CB_SKIP => {
self.slevel = 8;
if self.dispcache[p] != b'C' {
self.dispcache[p] = b'V';
}
}
PARANOIA_CB_OVERLAP => {
self.overlap = osector as i32;
}
PARANOIA_CB_SCRATCH => {
self.slevel = 7;
}
PARANOIA_CB_DRIFT => {
if self.slevel < 4 || self.stimeout > 5 {
self.slevel = 4;
}
}
PARANOIA_CB_FIXUP_DROPPED | PARANOIA_CB_FIXUP_DUPED => {
self.slevel = 5;
if matches!(self.dispcache[p], b' ' | b'-' | b'+') {
self.dispcache[p] = b'!';
}
}
_ => {}
}
}
let mut smilie = "= :-)";
match self.slevel {
0 => {
smilie = if self.skipped_flag { " 8-X" } else { " :^D" };
}
1 => smilie = " :-)",
2 => smilie = " :-|",
3 => smilie = " :-P",
4 => smilie = " :-/",
5 => smilie = " 8-|",
6 => smilie = " :-0",
7 => smilie = " :-(",
8 => {
smilie = " ;-(";
self.skipped_flag = true;
}
_ => {}
}
let test = Self::now_tenths();
if self.lasttime != test || function == -1 || self.slast != self.slevel {
if self.lasttime != test || function == -1 {
self.last += 1;
self.lasttime = test;
if self.last > 7 {
self.last = 0;
}
self.stimeout += 1;
self.heartbeat = match self.last {
0 => b' ',
1 | 7 => b'.',
2 | 6 => b'o',
3 | 5 => b'0',
4 => b'O',
_ => self.heartbeat,
};
if function == -1 {
self.heartbeat = b'*';
}
}
if self.slast != self.slevel {
self.stimeout = 0;
}
self.slast = self.slevel;
let mut buffer: Vec<u8> =
if self.abort_on_skip && self.skipped_flag && function != -1 {
format!(
"\r (== PROGRESS == [{}| {:06} {:02} ] =={} {} ==) ",
" ...aborting; please wait... ",
self.v_sector,
self.overlap as i64 / CD_FRAMEWORDS,
smilie,
self.heartbeat as char
)
.into_bytes()
} else {
let disp = String::from_utf8_lossy(&self.dispcache).to_string();
if self.v_sector == 0 {
format!(
"\r (== PROGRESS == [{}| ...... {:02} ] =={} {} ==) ",
disp,
self.overlap as i64 / CD_FRAMEWORDS,
smilie,
self.heartbeat as char
)
.into_bytes()
} else {
format!(
"\r (== PROGRESS == [{}| {:06} {:02} ] =={} {} ==) ",
disp,
self.v_sector,
self.overlap as i64 / CD_FRAMEWORDS,
smilie,
self.heartbeat as char
)
.into_bytes()
}
};
if aheadposition >= 0
&& aheadposition < GRAPH as i32
&& function != -1
&& buffer.len() > 19 + aheadposition as usize
{
buffer[19 + aheadposition as usize] = b'>';
}
let _ = io::stderr().write_all(&buffer);
if function == -1 {
let mut report_bytes = buffer[1..].to_vec();
report_bytes.extend_from_slice(b"\n\n");
log.write_summary_bytes(&report_bytes);
}
}
}
if function == -1 {
self.dispcache = [b' '; GRAPH];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn skip_flag_survives_redirected_output() {
let mut p = Progress::default();
let mut log = Log::new();
log.quiet = true;
p.callback(0, PARANOIA_CB_SKIP, &mut log);
assert!(p.skipped_flag);
}
}