1use std::fs::File;
4use std::io::prelude::*;
5use std::string::ToString;
6
7use cpclib_common::bitflags::bitflags;
8use cpclib_common::camino::Utf8Path;
9use cpclib_common::itertools::{Itertools, zip};
10use delegate::delegate;
11use getset::Getters;
12
13use crate::disc::Disc;
14
15#[allow(clippy::cast_possible_truncation)]
17#[allow(clippy::cast_sign_loss)]
18pub fn convert_real_sector_size_to_fdc_sector_size(mut size: u16) -> u8 {
19 let mut n = 0;
20 while size > 0x80 {
21 size >>= 1;
22 n += 1
23 }
24
25 n as _
26}
27
28pub fn convert_fdc_sector_size_to_real_sector_size(size: u8) -> u16 {
30 0x80 << size
31}
32
33#[derive(Debug, PartialEq, Copy, Clone, Ord, PartialOrd, Eq)]
34pub enum Head {
36 A,
38 B,
40 Unspecified
42}
43
44impl From<Head> for i32 {
45 fn from(val: Head) -> Self {
46 match val {
47 Head::A => 0,
48 Head::B => 1,
49 Head::Unspecified => 0
50 }
51 }
52}
53
54#[allow(missing_docs)]
55impl From<u8> for Head {
56 fn from(val: u8) -> Self {
57 match val {
58 0 => Self::A,
59 1 => Self::B,
60 _ => Self::Unspecified
61 }
62 }
63}
64
65#[allow(missing_docs)]
66impl From<Head> for u8 {
67 fn from(val: Head) -> Self {
68 match val {
69 Head::A | Head::Unspecified => 0,
70 Head::B => 1
71 }
72 }
73}
74
75#[allow(missing_docs)]
76impl From<&Head> for u8 {
77 fn from(val: &Head) -> Self {
78 match val {
79 Head::A | &Head::Unspecified => 0,
80 Head::B => 1
81 }
82 }
83}
84
85#[derive(Getters, Debug, Default, PartialEq, Clone)]
119pub struct DiscInformation {
120 #[get = "pub"]
122 pub(crate) creator_name: String,
123 #[get = "pub"]
125 pub(crate) number_of_tracks: u8,
126 #[get = "pub"]
128 pub(crate) number_of_heads: u8,
129 #[get = "pub"]
130 pub(crate) track_size_table: Vec<u8> }
133
134#[allow(missing_docs)]
135impl DiscInformation {
136 fn creator_name_as_bytes(&self) -> [u8; 14] {
137 let mut data = [0; 14];
138 for (idx, byte) in self.creator_name.as_bytes().iter().take(14).enumerate() {
139 data[idx] = *byte;
140 }
141 data
142 }
143
144 pub fn from_buffer(buffer: &[u8]) -> Self {
147 assert_eq!(buffer.len(), 256);
148 assert_eq!(
149 String::from_utf8_lossy(&buffer[..34]).to_ascii_uppercase(),
150 "EXTENDED CPC DSK File\r\nDisk-Info\r\n".to_ascii_uppercase()
151 );
152
153 let creator_name = String::from_utf8_lossy(&buffer[0x22..=0x2F]);
154 let number_of_tracks = buffer[0x30];
155 let number_of_heads = buffer[0x31];
156 let track_size_table = &buffer[0x34..(0x34 + number_of_tracks * number_of_heads) as usize];
157
158 assert!(number_of_heads == 1 || number_of_heads == 2);
159
160 Self {
161 creator_name: creator_name.to_string(),
162 number_of_tracks,
163 number_of_heads,
164 track_size_table: track_size_table.to_vec()
165 }
166 }
167
168 fn to_buffer(&self, buffer: &mut Vec<u8>) {
169 buffer.extend_from_slice("EXTENDED CPC DSK File\r\nDisk-Info\r\n".as_bytes());
170 assert_eq!(buffer.len(), 34);
171
172 buffer.extend_from_slice(&self.creator_name_as_bytes());
173 assert_eq!(buffer.len(), 34 + 14);
174
175 buffer.push(self.number_of_tracks);
176 buffer.push(self.number_of_heads);
177 assert_eq!(buffer.len(), 34 + 14 + 1 + 1);
178
179 buffer.push(0);
181 buffer.push(0);
182 assert_eq!(buffer.len(), 34 + 14 + 1 + 1 + 2);
183
184 buffer.extend_from_slice(&self.track_size_table);
185 assert_eq!(
186 buffer.len(),
187 34 + 14 + 1 + 1 + 2 + self.track_size_table.len()
188 );
189
190 assert!(buffer.len() <= 256);
191 buffer.resize(256, 0x00);
193 assert_eq!(buffer.len(), 256);
194
195 let from_buffer = Self::from_buffer(buffer);
197 assert_eq!(self, &from_buffer);
198 }
199
200 pub fn is_double_head(&self) -> bool {
202 self.number_of_heads == 2
203 }
204
205 pub fn is_single_head(&self) -> bool {
207 !self.is_double_head()
208 }
209
210 pub fn track_length(&self, track: u8, head: u8) -> u16 {
212 assert!(head < self.number_of_heads);
213
214 let track = track as usize;
215 let head = head as usize;
216 let idx = if self.is_single_head() {
217 track
218 }
219 else {
220 track * 2 + head
221 };
222
223 self.track_length_at_idx(idx)
224 }
225
226 pub fn is_formatted(&self, track: u8, head: u8) -> bool {
228 self.track_length(track, head) > 0
229 }
230
231 pub fn track_length_at_idx(&self, idx: usize) -> u16 {
233 256 * u16::from(self.track_size_table[idx])
234 }
235
236 pub fn total_tracks_lengths(&self) -> usize {
238 (0..self.number_of_distinct_tracks())
239 .map(|idx: usize| self.track_length_at_idx(idx) as usize)
240 .sum::<usize>()
241 }
242
243 pub fn number_of_distinct_tracks(&self) -> usize {
245 self.track_size_table.len()
246 }
247}
248
249#[allow(missing_docs)]
280#[derive(Getters, Debug, Default, PartialEq, Clone)]
281pub struct TrackInformation {
282 #[get = "pub"]
284 pub(crate) track_number: u8,
285 #[get = "pub"]
287 pub(crate) head_number: u8,
288 #[get = "pub"]
289 pub(crate) sector_size: u8, #[get = "pub"]
293 pub(crate) number_of_sectors: u8,
294 #[get = "pub"]
296 pub(crate) gap3_length: u8,
297 #[get = "pub"]
299 pub(crate) filler_byte: u8,
300 #[get = "pub"]
302 pub(crate) data_rate: DataRate,
303 #[get = "pub"]
305 pub(crate) recording_mode: RecordingMode,
306 #[get = "pub"]
308 pub(crate) sector_information_list: SectorInformationList,
309 #[get = "pub"]
311 pub(crate) track_size: u16
312}
313
314#[allow(missing_docs)]
315impl TrackInformation {
316 pub fn unformatted() -> Self {
318 Self::default()
319 }
320
321 pub fn real_track_size(&self) -> u16 {
323 self.track_size() - 256
324 }
325}
326
327#[allow(missing_docs)]
328impl TrackInformation {
329 delegate! {
330 to self.sector_information_list {
331 pub fn sector(&self, sector_id: u8) -> Option<&Sector>;
332 pub fn sector_mut(&mut self, sector_id: u8) -> Option<&mut Sector>;
333 }
334 }
335
336 #[deprecated(
337 note = "Note sure it should be used as each sector store this information and different sizes are possible"
338 )]
339 pub fn sector_size_human_readable(&self) -> u16 {
340 convert_fdc_sector_size_to_real_sector_size(self.sector_size)
341 }
342
343 pub fn next_sector_id(&self, sector: u8) -> Option<u8> {
345 for idx in 0..(self.number_of_sectors() - 1) {
346 let current_sector = &self.sector_information_list.sectors[idx as usize];
347 let next_sector = &self.sector_information_list.sectors[idx as usize + 1];
348
349 if *current_sector.sector_id() == sector {
350 return Some(*next_sector.sector_id());
351 }
352 }
353
354 None
355 }
356
357 pub fn min_sector(&self) -> u8 {
359 self.sector_information_list
360 .sectors()
361 .iter()
362 .map(|s| s.sector_information_bloc.sector_id)
363 .min()
364 .unwrap()
365 }
366
367 pub fn data_sum(&self) -> usize {
370 self.sector_information_list
371 .sectors
372 .iter()
373 .map(Sector::data_sum)
374 .sum::<usize>()
375 }
376
377 pub fn corresponds_to(&self, track: u8, head: u8) -> bool {
378 self.track_number == track && self.head_number == head
379 }
380
381 #[allow(clippy::cast_possible_truncation)]
382 pub fn from_buffer(buffer: &[u8]) -> Self {
383 if String::from_utf8_lossy(&buffer[..0xC]).to_ascii_uppercase()
384 != "Track-info\r\n".to_ascii_uppercase()
385 {
386 panic!(
387 "Track buffer does not seem coherent\n{:?}...",
388 &buffer[..0xC]
389 );
390 }
391
392 let track_size = buffer.len() as u16;
393 let track_number = buffer[0x10];
394 let head_number = buffer[0x11];
395 let sector_size = buffer[0x14];
396 let number_of_sectors = buffer[0x15];
397 let gap3_length = buffer[0x16];
398 let filler_byte = buffer[0x17];
399 let data_rate: DataRate = buffer[0x12].into();
400 let recording_mode = buffer[0x13].into();
401
402 println!(
403 "Track {track_number} Head {head_number} sector_size {sector_size} nb_sectors {number_of_sectors} gap length {gap3_length:x}, filler_byte {filler_byte:x}"
404 );
405 let sector_information_list =
406 SectorInformationList::from_buffer(&buffer[0x18..], number_of_sectors);
407
408 let track_info = Self {
409 track_number,
410 head_number,
411 sector_size,
412 number_of_sectors,
413 gap3_length,
414 filler_byte,
415 data_rate,
416 recording_mode,
417 sector_information_list,
418 track_size
419 };
420
421 assert!(track_info.track_size != 0);
422
423 assert_eq!(
424 track_info.real_track_size(),
425 track_info.compute_track_size() as u16,
426 "Wrong track_info {track_info:?}"
427 );
428 track_info
429 }
430
431 pub fn to_buffer(&self, buffer: &mut Vec<u8>) {
450 let start_size = buffer.len();
451
452 assert_eq!(start_size % 256, 0);
454
455 buffer.extend_from_slice(&"Track-Info\r\n".as_bytes()[..12]);
457 assert_eq!(buffer.len() - start_size, 12);
458
459 buffer.push(0);
461 buffer.push(0);
462 buffer.push(0);
463 buffer.push(0);
464
465 buffer.push(self.track_number);
467
468 buffer.push(self.head_number);
470
471 buffer.push(self.data_rate.into());
473
474 buffer.push(self.recording_mode.into());
476
477 buffer.push(self.sector_size);
479
480 buffer.push(self.number_of_sectors);
482
483 buffer.push(self.gap3_length);
485
486 buffer.push(self.filler_byte);
488
489 assert_eq!(buffer.len() - start_size, 0x18);
490
491 self.sector_information_list.sectors.iter().for_each(|s| {
494 s.sector_information_bloc.to_buffer(buffer);
495 });
496
497 let added_bytes = buffer.len() - start_size;
499 let missing_bytes = 256 - added_bytes;
500 buffer.resize(buffer.len() + missing_bytes, 0);
501 assert_eq!(buffer.len() % 256, 0);
502
503 self.sector_information_list.sectors.iter().for_each(|s| {
505 buffer.extend_from_slice(&s.values);
506 });
507
508 assert!(buffer.len().is_multiple_of(256));
521 }
522
523 pub fn total_size(&self) -> usize {
525 self.sector_information_list
526 .sectors
527 .iter()
528 .map(|info| info.sector_information_bloc.data_length as usize)
529 .sum()
530 }
531
532 pub fn compute_track_size(&self) -> usize {
534 let size = self.total_size();
535 if size.is_multiple_of(256) {
536 size
537 }
538 else {
539 let mut s = size;
540 while !s.is_multiple_of(256) {
542 s += 1;
543 }
544 s
545 }
546 }
547}
548
549#[derive(Debug, Copy, Clone, PartialEq)]
550#[allow(missing_docs)]
551pub enum DataRate {
552 Unknown = 0,
553 SingleOrDoubleDensity = 1,
554 HighDensity = 2,
555 ExtendedDensity = 3
556}
557
558#[allow(missing_docs)]
559impl Default for DataRate {
560 fn default() -> Self {
561 Self::Unknown
562 }
563}
564
565#[allow(missing_docs)]
566impl From<u8> for DataRate {
567 fn from(b: u8) -> Self {
568 match b {
569 0 => Self::Unknown,
570 1 => Self::SingleOrDoubleDensity,
571 2 => Self::HighDensity,
572 3 => Self::ExtendedDensity,
573 _ => unreachable!()
574 }
575 }
576}
577
578#[allow(missing_docs)]
579impl From<DataRate> for u8 {
580 fn from(val: DataRate) -> Self {
581 match val {
582 DataRate::Unknown => 0,
583 DataRate::SingleOrDoubleDensity => 1,
584 DataRate::HighDensity => 2,
585 DataRate::ExtendedDensity => 3
586 }
587 }
588}
589
590#[derive(Debug, Clone, Copy, PartialEq)]
591#[allow(missing_docs)]
592pub enum RecordingMode {
593 Unknown = 0,
594 FM = 1,
595 MFM = 2
596}
597
598#[allow(missing_docs)]
599impl Default for RecordingMode {
600 fn default() -> Self {
601 Self::Unknown
602 }
603}
604
605#[allow(missing_docs)]
606impl From<u8> for RecordingMode {
607 fn from(b: u8) -> Self {
608 match b {
609 0 => Self::Unknown,
610 1 => Self::FM,
611 2 => Self::MFM,
612 _ => unreachable!()
613 }
614 }
615}
616
617#[allow(missing_docs)]
618impl From<RecordingMode> for u8 {
619 fn from(val: RecordingMode) -> Self {
620 match val {
621 RecordingMode::Unknown => 0,
622 RecordingMode::FM => 1,
623 RecordingMode::MFM => 2
624 }
625 }
626}
627
628#[derive(Getters, Debug, Default, PartialEq, Clone, Copy)]
629#[allow(missing_docs)]
630pub struct SectorInformation {
631 #[get = "pub"]
633 pub(crate) track: u8,
634 #[get = "pub"]
636 pub(crate) head: u8,
637 #[get = "pub"]
639 pub(crate) sector_id: u8,
640 #[get = "pub"]
642 pub(crate) sector_size: u8,
643 #[get = "pub"]
645 pub(crate) fdc_status_register_1: u8,
646 #[get = "pub"]
648 pub(crate) fdc_status_register_2: u8,
649 #[get = "pub"]
651 pub(crate) data_length: u16 }
653
654#[allow(missing_docs)]
655#[allow(clippy::trivially_copy_pass_by_ref)]
656impl SectorInformation {
657 pub fn len(&self) -> usize {
659 self.sector_size as usize * 256
660 }
661
662 pub fn is_empty(&self) -> bool {
664 self.len() == 0
665 }
666
667 pub fn from_buffer(buffer: &[u8]) -> Self {
668 Self {
669 track: buffer[0x00],
670 head: buffer[0x01],
671 sector_id: buffer[0x02],
672 sector_size: buffer[0x03],
673 fdc_status_register_1: buffer[0x04],
674 fdc_status_register_2: buffer[0x05],
675 data_length: u16::from(buffer[0x06]) + (u16::from(buffer[0x07]) * 256)
676 }
677 }
678
679 #[allow(clippy::cast_possible_truncation)]
687 pub fn to_buffer(&self, buffer: &mut Vec<u8>) {
688 buffer.push(self.track);
689 buffer.push(self.head);
690 buffer.push(self.sector_id);
691 buffer.push(self.sector_size);
692 buffer.push(self.fdc_status_register_1);
693 buffer.push(self.fdc_status_register_2);
694
695 buffer.push((self.data_length % 256) as u8);
697 buffer.push((self.data_length / 256) as u8);
698 }
699}
700
701#[derive(Debug, Default, PartialEq, Clone)]
702#[allow(missing_docs)]
703pub struct SectorInformationList {
704 pub(crate) sectors: Vec<Sector>
706}
707
708#[allow(missing_docs)]
709impl SectorInformationList {
710 pub fn sectors(&self) -> &[Sector] {
711 &self.sectors
712 }
713
714 pub fn len(&self) -> usize {
716 self.sectors.len()
717 }
718
719 pub fn is_empty(&self) -> bool {
720 self.len() == 0
721 }
722
723 pub fn add_sector(&mut self, sector: Sector) {
725 self.sectors.push(sector);
726 }
727
728 pub fn from_buffer(buffer: &[u8], number_of_sectors: u8) -> Self {
729 let mut list_info = Vec::new();
730 let mut list_data = Vec::new();
731 let mut consummed_bytes = 0;
732
733 for _sector_number in 0..number_of_sectors {
735 let current_buffer = &buffer[consummed_bytes..];
736 let sector = SectorInformation::from_buffer(current_buffer);
737 consummed_bytes += 8;
738 list_info.push(sector);
739 }
740
741 consummed_bytes = 256 - 0x18; for sector in &list_info {
744 let current_sector_size = sector.data_length as usize;
745 let current_buffer = &buffer[consummed_bytes..consummed_bytes + current_sector_size];
746 let sector_bytes = current_buffer.to_vec();
747 assert_eq!(sector_bytes.len(), current_sector_size);
748 list_data.push(sector_bytes);
749 consummed_bytes += current_sector_size;
750 }
751
752 let info_drain = list_info.drain(..);
754 let data_drain = list_data.drain(..);
755 let sectors = zip(info_drain, data_drain)
756 .map(|(info, data)| {
757 assert_eq!(info.data_length as usize, data.len());
758 Sector {
759 sector_information_bloc: info,
760 values: data
761 }
762 })
763 .collect::<Vec<Sector>>();
764
765 Self { sectors }
766 }
767
768 pub fn sector(&self, sector_id: u8) -> Option<&Sector> {
769 self.sectors
770 .iter()
771 .find(|sector| sector.sector_information_bloc.sector_id == sector_id)
772 }
773
774 pub fn sector_mut(&mut self, sector_id: u8) -> Option<&mut Sector> {
776 self.sectors
777 .iter_mut()
778 .find(|sector| sector.sector_information_bloc.sector_id == sector_id)
779 }
780
781 #[allow(clippy::cast_possible_truncation)]
783 pub fn fill_with(
784 &mut self,
785 ids: &[u8],
786 heads: &[u8],
787 track_number: u8,
788 sector_size: u8,
789 filler_byte: u8
790 ) {
791 assert_eq!(ids.len(), heads.len());
792 assert_eq!(self.len(), 0);
793
794 for idx in 0..ids.len() {
795 let mut sector = Sector::default();
796
797 sector.sector_information_bloc.track = track_number;
798 sector.sector_information_bloc.sector_size = sector_size;
799 sector.sector_information_bloc.sector_id = ids[idx];
800 sector.sector_information_bloc.head = heads[idx];
801
802 let data_size = convert_fdc_sector_size_to_real_sector_size(
803 sector.sector_information_bloc.sector_size
804 ) as usize;
805 sector.values.resize(data_size, filler_byte);
806 sector.values.fill(filler_byte);
807 sector.sector_information_bloc.data_length = sector.values.len() as u16;
808
809 self.add_sector(sector);
810 }
811 }
812}
813
814bitflags! {
815 struct FdcStatusRegister1: u8 {
816 const END_OF_CYLINDER = 1<<7;
817 const DATA_ERROR = 1<<5;
818 const NO_DATA = 1<<2;
819 const MISSING_ADDRESS_MARK = 1<<0;
820 }
821}
822
823bitflags! {
824 struct FdcStatusRegister2: u8 {
825 const CONTROL_MARK = 1<<5;
826 const DATA_ERROR_IN_DATA_FIELD = 1<<5;
827 const MISSING_ADDRESS_MARK_IN_DATA_FIELD = 1<<0;
828 }
829}
830
831#[derive(Getters, Debug, Default, PartialEq, Clone)]
832#[allow(missing_docs)]
833#[allow(unused)]
834pub struct Sector {
835 #[getset(get)]
836 pub(crate) sector_information_bloc: SectorInformation,
837 pub(crate) values: Vec<u8>
839}
840
841#[allow(missing_docs)]
842impl Sector {
843 delegate! {
844 to self.sector_information_bloc {
845 pub fn sector_id(&self) -> &u8;
846 }
847 }
848
849 #[allow(clippy::cast_possible_truncation)]
851 pub fn real_size(&self) -> u16 {
852 self.values.len() as u16
853 }
854
855 pub fn len(&self) -> u16 {
856 self.sector_information_bloc.len() as u16
857 }
858
859 pub fn is_empty(&self) -> bool {
860 self.len() == 0
861 }
862
863 pub fn data_sum(&self) -> usize {
864 self.values().iter().map(|&v| v as usize).sum::<usize>()
865 }
866
867 pub fn values(&self) -> &[u8] {
868 &self.values[..self.len() as usize]
869 }
870
871 pub fn values_mut(&mut self) -> &mut [u8] {
872 let idx = self.len() as usize;
873 &mut self.values[..idx]
874 }
875
876 pub fn set_values(&mut self, data: &[u8]) -> Result<(), String> {
878 if data.len() < self.len() as usize {
879 return Err(format!(
880 "You cannot insert {} bytes in a sector of size {}.",
881 data.len(),
882 self.len()
883 ));
884 }
885
886 if data.len() > self.len() as usize {
887 return Err(format!(
888 "Smaller data of {} bytes to put in a sector of size {}.",
889 data.len(),
890 self.len()
891 ));
892 }
893
894 self.values[..].clone_from_slice(data);
895 Ok(())
896 }
897}
898
899#[derive(Default, PartialEq, Debug, Clone)]
900#[allow(missing_docs)]
901pub struct TrackInformationList {
902 pub(crate) list: Vec<TrackInformation>
903}
904
905#[allow(missing_docs)]
906impl TrackInformationList {
907 fn from_buffer_and_disc_information(buffer: &[u8], disc_info: &DiscInformation) -> Self {
908 let mut consummed_bytes: usize = 0;
909 let mut list = Vec::new();
910
911 for track_number in 0..disc_info.number_of_tracks {
912 for head_nb in 0..disc_info.number_of_heads {
913 let current_track_size = disc_info.track_length(track_number, head_nb) as usize;
915 let track_buffer = &buffer[consummed_bytes..(consummed_bytes + current_track_size)];
916 if current_track_size > 0 {
917 list.push(TrackInformation::from_buffer(track_buffer));
918 }
919 else {
920 eprintln!("Track {track_number} is unformatted");
921 list.push(TrackInformation::unformatted());
922 }
923 consummed_bytes += current_track_size;
924 }
925 }
926
927 Self { list }
928 }
929
930 fn to_buffer(&self, buffer: &mut Vec<u8>) {
932 for track in &self.list {
933 track.to_buffer(buffer);
934 }
935 }
936
937 pub fn add_empty_track(&mut self) -> &mut TrackInformation {
939 let track = TrackInformation::default();
940 self.list.push(track);
941 self.list.last_mut().unwrap()
942 }
943
944 pub fn tracks_for_head(&self, head: Head) -> impl Iterator<Item = &TrackInformation> {
946 let head: u8 = head.into();
947 self.list
948 .iter()
949 .filter(move |info| info.head_number == head)
950 }
951
952 pub fn next_track(&self, track: &TrackInformation) -> Option<&TrackInformation> {
954 for idx in 0..(self.list.len() - 1) {
955 let current_track = &self.list[idx];
956 let next_track = &self.list[idx + 1];
957
958 if current_track == track {
959 return Some(next_track);
960 }
961 }
962
963 None
964 }
965}
966
967#[derive(PartialEq, Debug, Clone)]
968#[allow(missing_docs)]
969pub struct ExtendedDsk {
970 pub(crate) disc_information_bloc: DiscInformation,
971 pub(crate) track_list: TrackInformationList
972}
973
974impl Default for ExtendedDsk {
975 fn default() -> Self {
976 let cfg = crate::cfg::DiscConfig::single_head_data42_format();
977
978 crate::builder::build_edsk_from_cfg(&cfg)
979 }
980}
981
982#[allow(missing_docs)]
983impl ExtendedDsk {
984 pub fn from_buffer(buffer: &[u8]) -> Self {
985 assert!(buffer.len() >= 256);
986 let disc_info = DiscInformation::from_buffer(&buffer[..256]);
987
988 println!(
989 "Disc info {:?} / total {} / nb_tracks {}",
990 disc_info,
991 disc_info.total_tracks_lengths(),
992 disc_info.number_of_distinct_tracks()
993 );
994 let track_list =
995 TrackInformationList::from_buffer_and_disc_information(&buffer[256..], &disc_info);
996
997 Self {
998 disc_information_bloc: disc_info,
999 track_list
1000 }
1001 }
1002
1003 pub fn add_file_sequentially(
1005 &mut self,
1006 head: u8,
1007 track: u8,
1008 sector: u8,
1009 buffer: &[u8]
1010 ) -> Result<(u8, u8, u8), String> {
1011 let mut pos = (head, track, sector);
1012 let mut consummed = 0;
1013 while consummed < buffer.len() {
1014 let current_sector = self
1015 .sector_mut(pos.0, pos.1, pos.2)
1016 .ok_or_else(|| "Sector not found".to_owned())?;
1017
1018 let sector_size = current_sector.len() as usize;
1019 let current_data = &buffer[consummed..consummed + sector_size];
1020 current_sector.set_values(current_data)?;
1021 consummed += sector_size;
1022
1023 let next_pos = self
1024 .next_position(pos.0, pos.1, pos.2)
1025 .ok_or_else(|| "No more position available".to_owned())?;
1026 pos = next_pos;
1027 }
1028
1029 Ok(pos)
1030 }
1031
1032 fn next_position(&self, head: u8, track: u8, sector: u8) -> Option<(u8, u8, u8)> {
1036 let current_track = self.get_track_information(
1038 head, track
1040 )?;
1041
1042 if let Some(next_sector) = current_track.next_sector_id(sector) {
1044 return Some((head, track, next_sector));
1045 }
1046
1047 let next_track = self.track_list.next_track(current_track)?;
1049
1050 Some((
1051 *next_track.head_number(), *next_track.track_number(),
1053 next_track.min_sector()
1054 ))
1055 }
1056
1057 pub fn to_buffer(&self, buffer: &mut Vec<u8>) {
1059 self.disc_information_bloc.to_buffer(buffer);
1060 self.track_list.to_buffer(buffer);
1061 }
1062
1063 pub fn is_double_head(&self) -> bool {
1064 self.disc_information_bloc.is_double_head()
1065 }
1066
1067 #[deprecated]
1068 pub fn nb_tracks_per_side(&self) -> u8 {
1069 self.nb_tracks_per_head()
1070 }
1071
1072 pub fn nb_heads(&self) -> u8 {
1073 self.disc_information_bloc.number_of_heads
1074 }
1075
1076 pub fn get_track_information<S: Into<Head>>(
1077 &self,
1078 head: S,
1079 track: u8
1080 ) -> Option<&TrackInformation> {
1081 let idx = self.get_track_idx(head.into(), track);
1082 self.track_list.list.get(idx)
1083 }
1084
1085 pub fn get_track_information_mut<S: Into<Head>>(
1086 &mut self,
1087 head: S,
1088 track: u8
1089 ) -> Option<&mut TrackInformation> {
1090 let idx = self.get_track_idx(head.into(), track);
1091 self.track_list.list.get_mut(idx)
1092 }
1093
1094 pub fn sector<S: Into<Head>>(&self, head: S, track: u8, sector_id: u8) -> Option<&Sector> {
1096 self.get_track_information(head.into(), track)
1097 .and_then(|track| track.sector(sector_id))
1098 }
1099
1100 pub fn sector_mut<S: Into<Head>>(
1102 &mut self,
1103 head: S,
1104 track: u8,
1105 sector_id: u8
1106 ) -> Option<&mut Sector> {
1107 self.get_track_information_mut(head.into(), track)
1108 .and_then(|track| track.sector_mut(sector_id))
1109 }
1110
1111 fn get_track_idx(&self, head: Head, track: u8) -> usize {
1112 if self.disc_information_bloc.is_double_head() {
1113 let head = match head {
1114 Head::A => 0,
1115 Head::B => 1,
1116 Head::Unspecified => panic!("You must specify a Head for a double Headed disc.")
1117 };
1118 track as usize * 2 + head
1119 }
1120 else {
1121 if let Head::B = head {
1122 panic!("You cannot select Head B in a single Head disc");
1123 }
1124 track as usize
1125 }
1126 }
1127
1128 pub fn track_bytes<H: Into<Head>>(&self, head: H, track: u8) -> Option<Vec<u8>> {
1130 match self.get_track_information(head, track) {
1131 Some(track) => {
1132 let mut bytes = Vec::new();
1133 for sector in track.sector_information_list.sectors() {
1134 bytes.extend(sector.values().iter());
1135 }
1136 Some(bytes)
1137 },
1138 _ => None
1139 }
1140 }
1141
1142 pub fn data_sum(&self, head: Head) -> usize {
1144 self.track_list
1145 .tracks_for_head(head)
1146 .map(TrackInformation::data_sum)
1147 .sum()
1148 }
1149
1150 pub fn tracks(&self) -> &[TrackInformation] {
1152 &self.track_list.list
1153 }
1154
1155 pub fn nb_tracks(&self) -> usize {
1157 self.tracks().len()
1158 }
1159}
1160
1161impl Disc for ExtendedDsk {
1162 fn open<P>(path: P) -> Result<Self, String>
1164 where P: AsRef<Utf8Path> {
1165 let path = path.as_ref();
1166 let buffer = {
1168 let mut f = File::open(path).map_err(|e| e.to_string())?;
1169 let mut buffer = Vec::new();
1170 f.read_to_end(&mut buffer).map_err(|e| e.to_string())?;
1171 buffer
1172 };
1173
1174 Ok(Self::from_buffer(&buffer))
1175 }
1176
1177 fn save<P>(&self, path: P) -> Result<(), String>
1179 where P: AsRef<Utf8Path> {
1180 let path = path.as_ref();
1181 let mut file_buffer = File::create(path).map_err(|e| e.to_string())?;
1182 let mut memory_buffer = Vec::new();
1183 self.to_buffer(&mut memory_buffer);
1184 file_buffer
1185 .write_all(&memory_buffer)
1186 .map_err(|e| e.to_string())
1187 }
1188
1189 fn global_min_sector<S: Into<Head>>(&self, _side: S) -> u8 {
1191 self.tracks()
1192 .iter()
1193 .map(TrackInformation::min_sector)
1194 .min()
1195 .unwrap()
1196 }
1197
1198 fn sector_read_bytes<S: Into<Head>>(
1199 &self,
1200 head: S,
1201 track: u8,
1202 sector_id: u8
1203 ) -> Option<Vec<u8>> {
1204 self.sector(head, track, sector_id)
1205 .map(|s| s.values.clone())
1206 }
1207
1208 fn sector_write_bytes<S: Into<Head>>(
1209 &mut self,
1210 head: S,
1211 track: u8,
1212 sector_id: u8,
1213 bytes: &[u8]
1214 ) -> Result<(), String> {
1215 let head = head.into();
1216 let sector = self.sector_mut(head, track, sector_id).ok_or_else(|| {
1217 format!(
1218 "Head {head:?} track {track} sector 0x{sector_id:X} missing",
1219 )
1220 }).unwrap();
1221 sector.set_values(bytes)?;
1222
1223 Ok(())
1224 }
1225
1226 fn track_min_sector<S: Into<Head>>(&self, side: S, track: u8) -> u8 {
1227 self.get_track_information(side, track)
1228 .unwrap()
1229 .min_sector()
1230 }
1231
1232 #[allow(clippy::cast_possible_truncation)]
1235 fn nb_tracks_per_head(&self) -> u8 {
1236 let val = if self.disc_information_bloc.is_single_head() {
1237 self.track_list.list.len()
1238 }
1239 else {
1240 self.track_list.list.len() / 2
1241 };
1242 (val & 0xFF) as u8
1243 }
1244}