131 lines
4.1 KiB
Rust
131 lines
4.1 KiB
Rust
use cdrustanoia_core::device::image::ImageDrive;
|
|
use cdrustanoia_core::device::{bytes_to_samples, samples_to_bytes, Drive, CD_FRAMESIZE_RAW};
|
|
use cdrustanoia_core::paranoia::{Paranoia, PARANOIA_MODE_FULL};
|
|
|
|
fn make_source(sectors: usize) -> Vec<i16> {
|
|
let mut out = Vec::with_capacity(sectors * 1176);
|
|
let mut x: u64 = 0x1234_5678_9abc_def0;
|
|
for i in 0..sectors * 1176 {
|
|
x ^= x << 13;
|
|
x ^= x >> 7;
|
|
x ^= x << 17;
|
|
// Mix in some silence and a distinct waveform.
|
|
let v = if i % 5000 < 1000 {
|
|
0
|
|
} else {
|
|
(x as i16).wrapping_add((i as i16).wrapping_mul(7))
|
|
};
|
|
out.push(v);
|
|
}
|
|
out
|
|
}
|
|
|
|
#[test]
|
|
fn clean_image_roundtrip() {
|
|
let sectors = 80usize;
|
|
let source = make_source(sectors);
|
|
let bytes = samples_to_bytes(&source);
|
|
let drive = ImageDrive::from_bytes(bytes);
|
|
let mut p = Paranoia::new(Box::new(drive));
|
|
p.modeset(PARANOIA_MODE_FULL);
|
|
let mut got = Vec::with_capacity(sectors * 1176);
|
|
let mut cb = |_: i64, _: i32| {};
|
|
for _ in 0..sectors {
|
|
let sec = p.read(&mut cb).expect("read sector");
|
|
got.extend_from_slice(sec);
|
|
}
|
|
assert_eq!(got.len(), source.len());
|
|
assert_eq!(got, source);
|
|
}
|
|
|
|
#[test]
|
|
fn drive_toc_helpers() {
|
|
let bytes = vec![0u8; CD_FRAMESIZE_RAW * 10];
|
|
let drive = ImageDrive::from_bytes(bytes);
|
|
assert_eq!(drive.tracks(), 1);
|
|
assert_eq!(drive.disc_firstsector(), 0);
|
|
assert_eq!(drive.disc_lastsector(), 9);
|
|
assert_eq!(drive.track_firstsector(1), 0);
|
|
assert_eq!(drive.track_lastsector(1), 9);
|
|
let _ = bytes_to_samples(&[0, 0]);
|
|
}
|
|
|
|
fn run_config(sectors: usize, config: cdrustanoia_core::device::image::ImageConfig) -> Vec<i16> {
|
|
let source = make_source(sectors);
|
|
let bytes = samples_to_bytes(&source);
|
|
let drive = ImageDrive::with_config(bytes, config);
|
|
let mut p = Paranoia::new(Box::new(drive));
|
|
p.modeset(PARANOIA_MODE_FULL);
|
|
let start = 5usize.min(sectors);
|
|
p.seek(start as i64, 0);
|
|
let mut got = Vec::with_capacity((sectors - start) * 1176);
|
|
let mut cb = |_: i64, _: i32| {};
|
|
for _ in start..sectors {
|
|
let sec = p.read(&mut cb).expect("read sector");
|
|
got.extend_from_slice(sec);
|
|
}
|
|
let source = &source[start * 1176..];
|
|
assert_eq!(got.len(), source.len());
|
|
if got != source {
|
|
let mut best: Option<(i64, usize)> = None;
|
|
for shift in -2000i64..=2000 {
|
|
let mut bad = 0usize;
|
|
for (i, value) in got.iter().enumerate().take(source.len()) {
|
|
let j = i as i64 + shift;
|
|
if j < 0 || j as usize >= source.len() || *value != source[j as usize] {
|
|
bad += 1;
|
|
if bad > 8 {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if bad <= 8 {
|
|
best = Some((shift, bad));
|
|
break;
|
|
}
|
|
}
|
|
if let Some((shift, bad)) = best {
|
|
panic!(
|
|
"data shifted by {} words ({} mismatches); engine did not align absolute position",
|
|
shift, bad
|
|
);
|
|
}
|
|
let idx = got
|
|
.iter()
|
|
.zip(source.iter())
|
|
.position(|(a, b)| a != b)
|
|
.unwrap_or(0);
|
|
let where0 = source.iter().position(|x| *x == got[0]);
|
|
panic!(
|
|
"mismatch at word {} (sector {} offset {}): got {} expected {}; got0 found at {:?}; got[..8]={:?} src[..8]={:?}",
|
|
idx,
|
|
idx / 1176,
|
|
idx % 1176,
|
|
got[idx],
|
|
source[idx],
|
|
where0,
|
|
&got[..got.len().min(8)],
|
|
&source[..source.len().min(8)]
|
|
);
|
|
}
|
|
got
|
|
}
|
|
|
|
#[test]
|
|
fn jittered_image_roundtrip() {
|
|
use cdrustanoia_core::device::image::{ImageConfig, ImageFaults};
|
|
let sectors = 20usize;
|
|
let config = ImageConfig {
|
|
tracks: vec![(0, sectors as i64 - 1, 0)],
|
|
faults: ImageFaults {
|
|
jitter_words: 180,
|
|
jitter_period: 1,
|
|
jitter_skip: 10,
|
|
max_read_sectors: 0,
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
};
|
|
run_config(sectors, config);
|
|
}
|