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 { 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 { 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); } #[test] fn fragmented_image_roundtrip() { use cdrustanoia_core::device::image::{ImageConfig, ImageFaults}; let sectors = 24usize; let config = ImageConfig { tracks: vec![(0, sectors as i64 - 1, 0)], faults: ImageFaults { max_read_sectors: 3, ..Default::default() }, ..Default::default() }; run_config(sectors, config); } #[test] fn silent_image_roundtrip() { use cdrustanoia_core::device::image::{ImageConfig, ImageFaults}; let sectors = 24usize; let config = ImageConfig { tracks: vec![(0, sectors as i64 - 1, 0)], faults: ImageFaults::default(), ..Default::default() }; let bytes = vec![0u8; sectors * CD_FRAMESIZE_RAW]; let drive = ImageDrive::with_config(bytes, config); let mut p = Paranoia::new(Box::new(drive)); p.modeset(PARANOIA_MODE_FULL); let mut cb = |_: i64, _: i32| {}; for _ in 0..sectors { let sec = p.read(&mut cb).expect("read silent sector"); assert!(sec.iter().all(|s| *s == 0)); } }