1use std::collections::BTreeMap;
3use std::convert::TryInto;
4use std::fs::File;
5use std::io::Write;
6use std::path::PathBuf;
7
8use crate::config::Config;
9use log::{debug, error};
10
11use nom::{
12 bytes::streaming::{take, take_until},
13 character::complete::one_of,
14 multi::many0,
15 number::complete::le_u8,
16 IResult,
17};
18
19use crate::disk_format::image::DiskImageSaver;
20
21pub enum Format {
40 FourAndFour,
47
48 FiveAndThree,
51
52 SixAndTwo,
57}
58
59#[allow(dead_code)]
61const NIBBLE_WRITE_TABLE_6_AND_2: [u8; 64] = [
62 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
63 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
64 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
65 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
66];
67
68const NIBBLE_READ_TABLE_6_AND_2: [u8; 256] = [
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
82 0x00, 0x00, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x1C, 0x1D, 0x1E,
84 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x21, 0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2A, 0x2B, 0x00, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
86 0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x00, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
87];
88
89pub fn parse_nibble_byte_4_and_4(i: &[u8]) -> IResult<&[u8], u8> {
93 let (i, bytes) = take(2_usize)(i)?;
94
95 let byte = ((bytes[0] << 1) | 0x01) & bytes[1];
96
97 Ok((i, byte))
98}
99
100pub struct AddressField {
102 pub volume: u8,
104 pub track: u8,
106 pub sector: u8,
108 pub checksum: u8,
110}
111
112pub fn parse_prologue(i: &[u8]) -> IResult<&[u8], &[u8]> {
115 let mut new_i = i;
118
119 loop {
120 let (i, _result) = take_until(&[0xD5, 0xAA][..])(new_i)?;
121
122 new_i = &i[2..];
124
125 let result = one_of::<&[u8], [u8; 2], crate::error::Error>([0x96_u8, 0xB5_u8])(new_i);
126 match result {
127 Ok(r) => {
128 debug!("Found an address field prologue");
129 return Ok((r.0, i));
130 }
131 Err(_e) => {}
133 }
134
135 }
137}
138
139pub fn recognize_prologue(i: &[u8]) -> Option<u8> {
142 let result: IResult<&[u8], &[u8]> = parse_prologue(i);
143
144 match result {
145 Ok(r) => Some(r.1[2]),
146 Err(_) => None,
147 }
148}
149
150pub fn find_and_parse_address_field(
152 config: &Config,
153) -> impl Fn(&[u8]) -> IResult<&[u8], AddressField> + '_ {
154 move |i| {
165 let (i, _data) = take_until(&[0xD5, 0xAA, 0x96][..])(i)?;
166 let (i, _prologue) = take(3_usize)(i)?;
167 let (i, volume) = parse_nibble_byte_4_and_4(i)?;
168 let (i, track) = parse_nibble_byte_4_and_4(i)?;
169 let (i, sector) = parse_nibble_byte_4_and_4(i)?;
170 let (i, checksum) = parse_nibble_byte_4_and_4(i)?;
171 let (i, _epilogue) = take(3_usize)(i)?;
172
173 debug!(
174 "Found address field: volume: {}, track: {}, sector: {}, checksum: {}",
175 volume, track, sector, checksum
176 );
177
178 let computed_checksum = volume ^ track ^ sector;
179
180 let address_field = AddressField {
181 volume,
182 track,
183 sector,
184 checksum,
185 };
186
187 if computed_checksum != checksum {
188 error!(
189 "Address field computed checksum not equal to disk checksum: {} {}",
190 computed_checksum, checksum
191 );
192 if !config
193 .settings
194 .get_bool("ignore-checksums")
195 .unwrap_or(false)
196 {
197 panic!(
198 "Address field computed checksum not equal to disk checksum: {} {}",
199 computed_checksum, checksum
200 );
201 }
202 }
203
204 Ok((i, address_field))
205 }
206}
207
208pub struct DataField {
210 _prologue: [u8; 3],
212 pub data: Vec<u8>,
214 pub checksum: u8,
216 _epilogue: [u8; 3],
218}
219
220pub fn parse_6_and_2_nibblized_data(i: &[u8]) -> Vec<u8> {
222 let data: Vec<u8> = i
223 .iter()
224 .map(|b| NIBBLE_READ_TABLE_6_AND_2[(*b & 0x7F) as usize])
225 .collect();
226
227 data
228}
229
230pub fn find_and_parse_data_field(i: &[u8]) -> IResult<&[u8], DataField> {
232 let (i, _data) = take_until(&[0xD5, 0xAA, 0xAD][..])(i)?;
236
237 let (i, prologue) = take(3_usize)(i)?;
243 let (i, data) = take(342_usize)(i)?;
244 let (i, checksum) = le_u8(i)?;
245 let (i, epilogue) = take(3_usize)(i)?;
247
248 Ok((
249 i,
250 DataField {
251 _prologue: prologue.try_into().unwrap(),
252 data: data.to_vec(),
253 checksum,
254 _epilogue: epilogue.try_into().unwrap(),
255 },
256 ))
257}
258
259#[derive(Clone)]
261pub struct Sector {
262 pub data: Vec<u8>,
264}
265
266pub fn data_field_build_buffer(data_field: &DataField) -> ([u8; 342], u8) {
268 let mut computed_checksum: u8 = 0;
272 let data_field_data_size = data_field.data.len();
273 let mut buffer = [0; 342];
274
275 for (index, byte) in data_field.data.iter().enumerate() {
276 computed_checksum ^= NIBBLE_READ_TABLE_6_AND_2[(*byte) as usize];
277 if index < 0x56 {
278 buffer[data_field_data_size - index - 1] = computed_checksum;
279 } else {
280 buffer[index - 0x56] = computed_checksum;
281 }
282 }
283 computed_checksum ^= NIBBLE_READ_TABLE_6_AND_2[data_field.checksum as usize];
284
285 (buffer, computed_checksum)
286}
287
288pub fn transform_data_field(config: &Config, data_field: &DataField) -> Sector {
290 let mut data = [0; 256];
298
299 let (buffer, computed_checksum) = data_field_build_buffer(data_field);
300
301 if computed_checksum != 0 {
302 error!(
303 "Invalid checksum on data: calculated: {}, disk: {}",
304 computed_checksum, data_field.checksum
305 );
306 if !config
307 .settings
308 .get_bool("ignore-checksums")
309 .unwrap_or(false)
310 {
311 panic!(
312 "Invalid checksum on data: calculated: {}, disk: {}",
313 computed_checksum, data_field.checksum
314 );
315 }
316 }
317
318 let reverse_values = [0x00, 0x02, 0x01, 0x03];
319 for i in 0..=255 {
320 let byte_1 = buffer[i];
321 let nibble_low = buffer.len() - (i % 0x56) - 1;
322 let byte_2 = buffer[nibble_low];
323 let shift_pairs = (i / 0x56) * 2;
324 let byte: u8 = (byte_1 << 2) | reverse_values[((byte_2 >> shift_pairs) & 0x03) as usize];
325 data[i] = byte;
326 }
327
328 Sector {
329 data: data.to_vec(),
330 }
331}
332
333pub fn build_nibble_sector(data: &[u8]) -> DataField {
346 let mut nibble_data: [u8; 344] = [0; 344];
348
349 nibble_data[86..(0xFF + 0x56 + 1)].copy_from_slice(&data[..(0xFF + 1)]);
356
357 let mut val: u8;
358
359 for (i, nibble_item) in nibble_data.iter_mut().enumerate().take(0x56) {
360 let ac_index: usize = (i + 0xAC) % 0x100;
361 let index_56: usize = (i + 0x56) % 0x100;
362 let index: usize = i % 0x100;
363 val = (((data[ac_index] & 0x1) << 1) | ((data[ac_index] & 0x2) >> 1)) << 6;
364 val |= (((data[index_56] & 0x1) << 1) | ((data[index_56] & 0x2) >> 1)) << 4;
365 val |= (((data[index] & 0x1) << 1) | ((data[index] & 0x2) >> 1)) << 2;
366 *nibble_item = val;
367 }
368
369 nibble_data[84] &= 0x3F;
370 nibble_data[85] &= 0x3F;
371
372 let mut checksum: u8 = 0;
373 let mut saved_data: u8;
374 for item in &mut nibble_data {
375 saved_data = *item;
376 *item ^= checksum;
377 checksum = saved_data;
378 }
379
380 let final_data: [u8; 342] = nibble_data[0..=341]
381 .iter()
382 .map(|d| NIBBLE_WRITE_TABLE_6_AND_2[(d >> 2) as usize])
383 .collect::<Vec<u8>>()
384 .try_into()
385 .unwrap();
386
387 DataField {
388 _prologue: [0xD5, 0xAA, 0xAD],
389 data: final_data.to_vec(),
390 checksum,
391 _epilogue: [0xDE, 0xAA, 0xEB],
392 }
393}
394
395pub fn nibblize_data(data: &[u8]) -> Vec<u8> {
397 let mut output_data: Vec<u8> = Vec::new();
398
399 let mut i = 0;
400 debug!("Data length: {}", data.len());
401 while (i + 256) < data.len() {
402 let block = &data[i..=(i + 255)];
403 output_data.append(&mut build_nibble_sector(block).data);
404 i += 256;
405 }
406
407 if i > 0 {
408 i -= 256;
409 }
410 let block = &data[i..data.len()];
411 output_data.append(&mut build_nibble_sector(block).data);
412
413 output_data
414}
415
416#[derive(Default)]
418pub struct Track {
419 pub sectors: BTreeMap<u8, Sector>,
421}
422
423#[derive(Default)]
425pub struct Volume {
426 pub tracks: BTreeMap<u8, Track>,
428}
429
430#[derive(Default)]
434pub struct NibbleDisk {
435 pub volumes: BTreeMap<u8, Volume>,
437}
438
439impl DiskImageSaver for NibbleDisk {
468 fn save_disk_image(
469 &self,
470 _config: &Config,
471 _selected_filename: Option<&str>,
472 filename: &str,
473 ) -> std::result::Result<(), crate::error::Error> {
474 let filename = PathBuf::from(filename);
475 let file_result = File::create(filename);
476 match file_result {
477 Ok(mut file) => {
478 for volume in self.volumes.values() {
479 for track in volume.tracks.values() {
480 for sector in track.sectors.values() {
481 file.write_all(§or.data).unwrap();
482 }
483 }
484 }
485 }
486 Err(e) => error!("Error opening file: {}", e),
487 }
488 Ok(())
489 }
490}
491
492pub struct Field {
494 pub address_field: AddressField,
498 pub data_field: DataField,
500}
501
502pub fn parse_nib_sector(config: &Config) -> impl Fn(&[u8]) -> IResult<&[u8], Field> + '_ {
504 move |i| {
505 let (i, header) = find_and_parse_address_field(config)(i)?;
506 let (i, data_field) = find_and_parse_data_field(i)?;
507
508 Ok((
509 i,
510 Field {
511 address_field: header,
512 data_field,
513 },
514 ))
515 }
516}
517
518pub fn parse_nib_disk(config: &Config) -> impl Fn(&[u8]) -> IResult<&[u8], NibbleDisk> + '_ {
520 move |i| {
521 let (i, fields) = many0(parse_nib_sector(config))(i)?;
522
523 debug!("Found {} fields", fields.len());
524 let mut disk = NibbleDisk::default();
525
526 for field in &fields {
527 debug!("Parsing another field");
528 let volume = disk.volumes.entry(field.address_field.volume);
529 let track = volume.or_default().tracks.entry(field.address_field.track);
530 let sector = track.or_default().sectors.entry(field.address_field.sector);
531 sector.or_insert_with(|| transform_data_field(config, &field.data_field));
532 }
533
534 Ok((i, disk))
535 }
536}
537
538#[cfg(test)]
539mod tests {
540 use super::{
541 build_nibble_sector, data_field_build_buffer, find_and_parse_address_field,
542 parse_nibble_byte_4_and_4, parse_prologue, transform_data_field, DataField,
543 NIBBLE_WRITE_TABLE_6_AND_2,
544 };
545 use crate::config::{Config, Configuration};
546 use pretty_assertions::assert_eq;
547
548 #[test]
550 fn parse_nibble_byte_4_and_4_works() {
551 let volume_data: [u8; 2] = [0xFF, 0xFE];
552 let track_data: [u8; 2] = [0xAB, 0xBF];
553 let sector_data: [u8; 2] = [0xAA, 0xAF];
554 let checksum_data: [u8; 2] = [0xFE, 0xEE];
555
556 let zero_data: [u8; 2] = [0x00, 0x00];
557 let one_data: [u8; 2] = [0x00, 0x01];
558
559 let volume_result = parse_nibble_byte_4_and_4(&volume_data);
560 match volume_result {
561 Ok(volume) => {
562 assert_eq!(volume.1, 0xFE);
563 }
564 Err(e) => {
565 panic!("Parser failed: {}", e);
566 }
567 }
568
569 let track_result = parse_nibble_byte_4_and_4(&track_data);
570 match track_result {
571 Ok(track) => {
572 assert_eq!(track.1, 0x17);
573 }
574 Err(e) => {
575 panic!("Parser failed: {}", e);
576 }
577 }
578
579 let sector_result = parse_nibble_byte_4_and_4(§or_data);
580 match sector_result {
581 Ok(sector) => {
582 assert_eq!(sector.1, 0x05);
583 }
584 Err(e) => {
585 panic!("Parser failed: {}", e);
586 }
587 }
588
589 let checksum_result = parse_nibble_byte_4_and_4(&checksum_data);
590 match checksum_result {
591 Ok(checksum) => {
592 assert_eq!(checksum.1, 0xEC);
593 }
594 Err(e) => {
595 panic!("Parser failed: {}", e);
596 }
597 }
598
599 let zero_result = parse_nibble_byte_4_and_4(&zero_data);
600 match zero_result {
601 Ok(zero) => {
602 assert_eq!(zero.1, 0x00);
603 }
604 Err(e) => {
605 panic!("Parser failed: {}", e);
606 }
607 }
608
609 let one_result = parse_nibble_byte_4_and_4(&one_data);
610 match one_result {
611 Ok(one) => {
612 assert_eq!(one.1, 0x01);
613 }
614 Err(e) => {
615 panic!("Parser failed: {}", e);
616 }
617 }
618 }
619
620 #[test]
622 fn find_and_parse_address_field_works() {
623 let address_field_data: [u8; 14] = [
625 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0xAB, 0xBF, 0xAA, 0xAF, 0xFE, 0xEE, 0xDE, 0xAA, 0xEB,
626 ];
627
628 let config = Config::load(config::Config::default()).unwrap();
629 let address_field_result = find_and_parse_address_field(&config)(&address_field_data);
630
631 match address_field_result {
632 Ok(address_field) => {
633 assert_eq!(address_field.1.volume, 0xFE);
634 assert_eq!(address_field.1.track, 0x17);
635 assert_eq!(address_field.1.sector, 0x05);
636 assert_eq!(address_field.1.checksum, 0xEC);
637 }
638 Err(e) => {
639 panic!("Parsing error: {}", e);
640 }
641 }
642 }
643
644 #[test]
647 pub fn transform_data_field_works() {
648 let mut data: [u8; 342] = [0; 342];
649
650 for i in 0..=341 {
651 data[i] = NIBBLE_WRITE_TABLE_6_AND_2[i % 0x40];
653 }
654
655 let data_field = DataField {
656 _prologue: [0xD5, 0xAA, 0xAD],
657 data: data.to_vec(),
658 checksum: 0x96,
659 _epilogue: [0xDE, 0xAA, 0xEB],
660 };
661
662 let (_buffer, checksum) = data_field_build_buffer(&data_field);
663
664 assert_eq!(checksum, 1);
665 }
666
667 #[test]
674 pub fn data_field_round_trip() {
675 let mut original_data: [u8; 256] = [0; 256];
676
677 for i in 0_u8..=255_u8 {
678 original_data[i as usize] = i;
679 }
680 original_data[255] = 1;
681
682 let data_field = build_nibble_sector(&original_data);
683
684 let config = Config::load(config::Config::default()).unwrap();
685 let sector = transform_data_field(&config, &data_field);
686
687 assert_eq!(sector.data, original_data);
688 }
689
690 #[test]
692 #[should_panic(expected = "Address field computed checksum not equal to disk checksum: 236 0")]
693 fn find_and_parse_address_field_panics_with_invalid_checksum() {
694 let address_field_data: [u8; 14] = [
696 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0xAB, 0xBF, 0xAA, 0xAF, 0x00, 0x00, 0xDE, 0xAA, 0xEB,
697 ];
698
699 let config = Config::load(config::Config::default()).unwrap();
700 let address_field_result = find_and_parse_address_field(&config)(&address_field_data);
701
702 match address_field_result {
703 Ok(_address_field) => {
704 panic!("Should fail with checksum error");
705 }
706 Err(_e) => {
707 panic!("Should fail with checksum error");
708 }
709 }
710 }
711
712 #[test]
716 fn parse_prologue_dos_33_works() {
717 let data = [0xD5, 0xAA, 0x96];
718
719 let result = parse_prologue(&data);
720
721 match result {
722 Ok(r) => {
723 assert_eq!(r.1, &[0xD5, 0xAA, 0x96]);
724 }
725 Err(_) => {
726 panic!("Shouldn't fail parsing data");
727 }
728 }
729 }
730
731 #[test]
734 fn parse_prologue_dos_33_skip_works() {
735 let data = [0x00, 0x00, 0xD5, 0xAA, 0x96];
736
737 let result = parse_prologue(&data);
738
739 match result {
740 Ok(r) => {
741 assert_eq!(r.1, &[0xD5, 0xAA, 0x96]);
742 }
743 Err(_) => {
744 panic!("Shouldn't fail parsing data");
745 }
746 }
747 }
748
749 #[test]
751 fn parse_prologue_dos_32_works() {
752 let data = [0xD5, 0xAA, 0xB5];
753
754 let result = parse_prologue(&data);
755
756 match result {
757 Ok(r) => {
758 assert_eq!(r.1, &[0xD5, 0xAA, 0xB5]);
759 }
760 Err(_) => {
761 panic!("Shouldn't fail parsing data");
762 }
763 }
764 }
765
766 #[test]
768 fn parse_no_prologue_fails() {
769 let data = [0x00, 0x00, 0x00];
770
771 let result = parse_prologue(&data);
772
773 match result {
774 Ok(_) => {
775 panic!("Should fail parsing data");
776 }
777 Err(e) => match e {
778 nom::Err::Incomplete(nom::Needed::Unknown) => {
779 assert_eq!("Parsing requires more data", e.to_string());
780 }
781 _ => {
782 panic!("Wrong parsing error");
783 }
784 },
785 }
786 }
787
788 #[test]
791 fn parse_incomplete_prologue_fails() {
792 let data = [0xD5, 0xAA, 0x00];
793
794 let result = parse_prologue(&data);
795
796 match result {
797 Ok(_) => {
798 panic!("Should fail parsing data");
799 }
800 Err(e) => match e {
801 nom::Err::Incomplete(nom::Needed::Unknown) => {
802 assert_eq!("Parsing requires more data", e.to_string());
803 }
804 _ => {
805 panic!("Wrong parsing error");
806 }
807 },
808 }
809 }
810
811 #[test]
814 fn parse_incomplete_prologue_two_bytes_fails() {
815 let data = [0xD5, 0xAA];
816
817 let result = parse_prologue(&data);
818
819 match result {
820 Ok(_) => {
821 panic!("Should fail parsing data");
822 }
823 Err(e) => match e {
824 nom::Err::Incomplete(nom::Needed::Unknown) => {
825 assert_eq!("Parsing requires more data", e.to_string());
826 }
827 _ => {
828 panic!("Wrong parsing error");
829 }
830 },
831 }
832 }
833}