1use a2kit_macro::DiskStructError;
11use crate::img;
12use crate::img::{Track,Sector,SectorHood};
13use crate::img::tracks::{Method,FluxCells};
14use crate::img::tracks::gcr::TrackEngine;
15use crate::{STDRESULT,DYNERR};
16
17pub const TRACK_BYTE_CAPACITY_NIB: usize = 6656;
18pub const TRACK_BYTE_CAPACITY_NB2: usize = 6384;
19
20pub fn file_extensions() -> Vec<String> {
21 vec!["nib".to_string(),"nb2".to_string()]
22}
23
24pub struct Nib {
25 kind: img::DiskKind,
26 fmt: Option<img::tracks::DiskFormat>,
27 tracks: usize,
28 trk_cap: usize,
29 data: Vec<u8>,
30 engine: TrackEngine,
32 cells: FluxCells,
34 track_pos: usize,
36}
37
38impl Nib {
39 pub fn create(vol: u8,kind: img::DiskKind) -> Result<Self,DYNERR> {
42 let fmt = match kind {
43 img::names::A2_DOS32_KIND => img::tracks::DiskFormat::apple_525_13(8),
44 img::names::A2_DOS33_KIND => img::tracks::DiskFormat::apple_525_16(8),
45 _ => {
46 log::error!("Nib can only accept 5.25 inch Apple formats");
47 return Err(Box::new(img::Error::ImageTypeMismatch));
48 }
49 };
50 let mut data: Vec<u8> = Vec::new();
51 let mut init_cells = None;
52 let mut engine = TrackEngine::create(Method::Fast,true);
53 for track in 0..35 {
54 let hood = SectorHood::a2_525(vol, track);
55 let zfmt = fmt.get_zone_fmt(0, 0)?;
56 let cells = engine.format_track(hood, TRACK_BYTE_CAPACITY_NIB,&zfmt)?;
57 let (mut buf,_) = cells.to_woz_buf(Some(TRACK_BYTE_CAPACITY_NIB),0xff);
58 if track==0 {
59 init_cells = Some(cells);
60 }
61 data.append(&mut buf);
62 }
63 Ok(Self {
64 kind,
65 fmt: Some(fmt),
66 tracks: 35,
67 trk_cap: TRACK_BYTE_CAPACITY_NIB,
68 data,
69 engine,
70 cells: init_cells.unwrap(),
71 track_pos: 0,
72 })
73 }
74 fn try_track(&self,trk: Track) -> Result<usize,DYNERR> {
75 match trk {
76 Track::Motor((m,h)) if m%4==0 && m < 140 && h==0 => Ok(m/4),
77 Track::CH((c,h)) if c < 35 && h==0 => Ok(c),
78 Track::Num(t) if t < 35 => Ok(t),
79 _ => {
80 log::error!("Nib image could not handle track key {}",trk);
81 Err(Box::new(img::Error::ImageTypeMismatch))
82 }
83 }
84 }
85 fn get_trk_bits_ref(&self,trk: Track) -> Result<&[u8],DYNERR> {
87 let track = self.try_track(trk)?;
88 Ok(&self.data[track * self.trk_cap..(track+1) * self.trk_cap])
89 }
90 fn get_trk_bits_mut(&mut self,trk: Track) -> Result<&mut [u8],DYNERR> {
92 let track = self.try_track(trk)?;
93 Ok(&mut self.data[track * self.trk_cap..(track+1) * self.trk_cap])
94 }
95 fn write_back_track(&mut self) {
97 let track = self.track_pos;
98 let (src,_) = self.cells.to_woz_buf(Some(self.trk_cap), 0xff);
99 self.data[track * self.trk_cap..(track+1) * self.trk_cap].copy_from_slice(&src);
100 }
101 fn goto_track(&mut self,trk: Track) -> Result<[usize;3],DYNERR> {
103 let track = self.try_track(trk)?;
104 if self.track_pos != track {
105 log::debug!("goto track {} of {}",track,self.kind);
106 self.write_back_track();
107 self.track_pos = track;
108 let bit_count = self.trk_cap * 8;
109 self.cells = FluxCells::from_woz_bits(bit_count,self.get_trk_bits_ref(trk)?,0,false);
110 }
111 img::woz::get_motor_pos(trk, &self.kind)
112 }
113}
114
115impl img::DiskImage for Nib {
116 fn track_count(&self) -> usize {
117 self.tracks
118 }
119 fn end_track(&self) -> usize {
120 self.tracks
121 }
122 fn num_heads(&self) -> usize {
123 1
124 }
125 fn nominal_capacity(&self) -> Option<usize> {
126 match self.kind {
127 img::names::A2_DOS32_KIND => Some(self.tracks*13*256),
128 img::names::A2_DOS33_KIND => Some(self.tracks*16*256),
129 _ => None
130 }
131 }
132 fn actual_capacity(&mut self) -> Result<usize,DYNERR> {
133 match self.nominal_capacity() {
134 Some(ans) => Ok(ans),
135 None => Err(Box::new(img::Error::DiskKindMismatch))
136 }
137 }
138 fn what_am_i(&self) -> img::DiskImageType {
139 img::DiskImageType::NIB
140 }
141 fn file_extensions(&self) -> Vec<String> {
142 file_extensions()
143 }
144 fn kind(&self) -> img::DiskKind {
145 self.kind
146 }
147 fn change_kind(&mut self,kind: img::DiskKind) {
148 self.kind = kind;
149 }
150 fn read_block(&mut self,addr: crate::bios::Block) -> Result<Vec<u8>,DYNERR> {
151 crate::bios::blocks::apple::read_block(self, addr)
152 }
153 fn write_block(&mut self, addr: crate::bios::Block, dat: &[u8]) -> STDRESULT {
154 crate::bios::blocks::apple::write_block(self, addr, dat)
155 }
156 fn read_sector(&mut self,trk: Track,sec: Sector) -> Result<Vec<u8>,DYNERR> {
157 if self.fmt.is_none() {
158 return Err(Box::new(img::Error::UnknownDiskKind));
159 }
160 self.goto_track(trk)?;
161 let [motor,head,_] = img::woz::get_motor_pos(trk, &self.kind)?;
162 let fmt = self.fmt.as_ref().unwrap(); let zfmt = fmt.get_zone_fmt(motor,head)?;
164 let hood = SectorHood::a2_525(254, u8::try_from((motor+1)/4)?);
165 let ans = self.engine.read_sector(&mut self.cells,&hood,&sec,zfmt)?;
166 Ok(ans)
167 }
168 fn write_sector(&mut self,trk: Track,sec: Sector,dat: &[u8]) -> Result<(),DYNERR> {
169 if self.fmt.is_none() {
170 return Err(Box::new(img::Error::UnknownDiskKind));
171 }
172 let [motor,head,_] = self.goto_track(trk)?;
173 let fmt = self.fmt.as_ref().unwrap(); let zfmt = fmt.get_zone_fmt(motor,head)?.clone();
175 let hood = SectorHood::a2_525(254, u8::try_from((motor+1)/4)?);
176 self.engine.write_sector(&mut self.cells,dat,&hood,&sec,&zfmt)?;
177 Ok(())
178 }
179 fn from_bytes(buf: &[u8]) -> Result<Self,DiskStructError> where Self: Sized {
180 match buf.len() {
181 l if l==35*TRACK_BYTE_CAPACITY_NIB => {
182 let data = buf.to_vec();
183 let cells = FluxCells::from_woz_bits(TRACK_BYTE_CAPACITY_NIB*8, &data[0..TRACK_BYTE_CAPACITY_NIB],0,false);
184 let mut disk = Self {
185 kind: img::names::A2_DOS33_KIND,
186 fmt: Some(img::tracks::DiskFormat::apple_525_16(8)),
187 tracks: 35,
188 trk_cap: TRACK_BYTE_CAPACITY_NIB,
189 data,
190 engine: TrackEngine::create(Method::Fast, true),
191 cells,
192 track_pos: 0,
193 };
194 match disk.get_track_solution(Track::Num(0)) { Ok(_) => {
195 log::debug!("setting disk kind to {}",disk.kind);
196 return Ok(disk);
197 } _ => {
198 log::debug!("Looks like NIB, but could not solve track 0");
199 return Err(DiskStructError::UnexpectedValue);
200 }}
201 },
202 l if l==35*TRACK_BYTE_CAPACITY_NB2 => {
203 let data = buf.to_vec();
204 let cells = FluxCells::from_woz_bits(TRACK_BYTE_CAPACITY_NB2*8, &data[0..TRACK_BYTE_CAPACITY_NB2],0,false);
205 let mut disk = Self {
206 kind: img::names::A2_DOS33_KIND,
207 fmt: Some(img::tracks::DiskFormat::apple_525_16(8)),
208 tracks: 35,
209 trk_cap: TRACK_BYTE_CAPACITY_NB2,
210 data,
211 engine: TrackEngine::create(Method::Fast, true),
212 cells,
213 track_pos: usize::MAX
214 };
215 match disk.get_track_solution(Track::Num(0)) { Ok(_) => {
216 log::debug!("setting disk kind to {}",disk.kind);
217 return Ok(disk);
218 } _ => {
219 log::debug!("Looks like NB2, but could not solve track 0");
220 return Err(DiskStructError::UnexpectedValue);
221 }}
222 }
223 _ => {
224 log::debug!("Buffer size {} fails to match nib or nb2",buf.len());
225 Err(DiskStructError::UnexpectedSize)
226 }
227 }
228 }
229 fn to_bytes(&mut self) -> Vec<u8> {
230 self.write_back_track();
231 self.data.clone()
232 }
233 fn get_track_buf(&mut self,trk: Track) -> Result<Vec<u8>,DYNERR> {
234 Ok(self.get_trk_bits_ref(trk)?.to_vec())
235 }
236 fn set_track_buf(&mut self,trk: Track,dat: &[u8]) -> STDRESULT {
237 let bits = self.get_trk_bits_mut(trk)?;
238 if bits.len()!=dat.len() {
239 log::error!("source track buffer is {} bytes, destination track buffer is {} bytes",dat.len(),bits.len());
240 return Err(Box::new(img::Error::ImageSizeMismatch));
241 }
242 bits.copy_from_slice(dat);
243 Ok(())
244 }
245 fn get_track_solution(&mut self,trk: Track) -> Result<img::TrackSolution,DYNERR> {
246 let [motor,head,_] = self.goto_track(trk)?;
247 if let Some(fmt) = &self.fmt {
249 log::trace!("try current format");
250 let zfmt = fmt.get_zone_fmt(motor,head)?;
251 if let Ok((addr_map,size_map)) = self.engine.get_sector_map(&mut self.cells,zfmt) {
252 return Ok(zfmt.track_solution(addr_map,size_map,"VTSK",[255,255,255,255,0,0],None));
253 }
254 }
255 log::trace!("try DOS 3.2 format");
257 self.kind = img::names::A2_DOS32_KIND;
258 self.fmt = img::woz::kind_to_format(&self.kind);
259 let zfmt = img::tracks::get_zone_fmt(motor,head,&self.fmt)?;
260 if let Ok((addr_map,size_map)) = self.engine.get_sector_map(&mut self.cells,zfmt) {
261 if addr_map.len()==13 {
262 return Ok(zfmt.track_solution(addr_map,size_map,"VTSK",[255,255,255,255,0,0],None));
263 }
264 }
265 log::trace!("try DOS 3.3 format");
266 self.kind = img::names::A2_DOS33_KIND;
267 self.fmt = img::woz::kind_to_format(&self.kind);
268 let zfmt = img::tracks::get_zone_fmt(motor,head,&self.fmt)?;
269 if let Ok((addr_map,size_map)) = self.engine.get_sector_map(&mut self.cells,zfmt) {
270 if addr_map.len()==16 {
271 return Ok(zfmt.track_solution(addr_map,size_map,"VTSK",[255,255,255,255,0,0],None));
272 }
273 }
274 return Ok(img::TrackSolution::Unsolved);
275 }
276 fn export_geometry(&mut self,indent: Option<u16>) -> Result<String,DYNERR> {
277 let pkg = img::package_string(&self.kind());
278 let mut track_sols = Vec::new();
279 for track in 0..self.tracks {
280 let sol = self.get_track_solution(Track::Num(track))?;
281 let [c,h] = self.get_rz(Track::Num(track))?;
282 track_sols.push((c as f64,h,sol));
283 }
284 img::geometry_json(pkg,track_sols,35,1,1,indent)
285 }
286 fn export_format(&self,indent: Option<u16>) -> Result<String,DYNERR> {
287 match &self.fmt {
288 Some(f) => f.to_json(indent),
289 None => Err(Box::new(super::Error::UnknownFormat))
290 }
291 }
292 fn get_track_nibbles(&mut self,trk: Track) -> Result<Vec<u8>,DYNERR> {
293 let [motor,head,_] = self.goto_track(trk)?;
294 let zfmt = img::tracks::get_zone_fmt(motor, head, &self.fmt)?;
295 Ok(self.engine.to_nibbles(&mut self.cells, zfmt))
296 }
297 fn display_track(&self,bytes: &[u8]) -> String {
298 let trk = Track::Num(self.track_pos);
299 let [motor,head,_] = img::woz::get_motor_pos(trk, &self.kind).expect("could not get head position");
300 let zfmt = match img::tracks::get_zone_fmt(motor, head, &self.fmt) {
301 Ok(z) => Some(z),
302 _ => None
303 };
304 super::woz::track_string_for_display(0, &bytes, zfmt)
305 }
306}