1use log::debug;
8use crate::{STDRESULT,DYNERR};
9
10use a2kit_macro::{DiskStructError,DiskStruct};
14use a2kit_macro_derive::DiskStruct;
15
16use super::fat::FIRST_DATA_CLUSTER;
17
18const JMP_BOOT: [u8;3] = [0xeb,0x58,0x90];
19const OEM_NAME: [u8;8] = *b"A2KITX.X";
20const BOOT_SIGNATURE: [u8;2] = [0x55,0xaa]; const RCH: &str = "unreachable was reached";
22
23#[derive(DiskStruct)]
29pub struct BPBFoundation {
30 pub bytes_per_sec: [u8;2],
32 pub sec_per_clus: u8,
35 pub reserved_sectors: [u8;2],
37 pub num_fats: u8,
39 pub root_ent_cnt: [u8;2],
43 pub tot_sec_16: [u8;2],
46 pub media: u8,
57 pub fat_size_16: [u8;2],
59 pub sec_per_trk: [u8;2],
61 pub num_heads: [u8;2],
63 pub hidd_sec: [u8;4],
66 pub tot_sec_32: [u8;4],
69}
70
71impl BPBFoundation {
72 fn to_json(&self,indent: Option<u16>) -> String {
73 let mut ans = json::JsonValue::new_object();
74 let mut bpb = json::JsonValue::new_object();
75 bpb["bytes_per_sec"] = json::JsonValue::String(hex::encode_upper(&self.bytes_per_sec));
76 bpb["sec_per_clus"] = json::JsonValue::String(hex::encode_upper(&vec![self.sec_per_clus]));
77 bpb["reserved_sectors"] = json::JsonValue::String(hex::encode_upper(&self.reserved_sectors));
78 bpb["num_fats"] = json::JsonValue::String(hex::encode_upper(&vec![self.num_fats]));
79 bpb["root_ent_cnt"] = json::JsonValue::String(hex::encode_upper(&self.root_ent_cnt));
80 bpb["tot_sec_16"] = json::JsonValue::String(hex::encode_upper(&self.tot_sec_16));
81 bpb["media"] = json::JsonValue::String(hex::encode_upper(&vec![self.media]));
82 bpb["fat_size_16"] = json::JsonValue::String(hex::encode_upper(&self.fat_size_16));
83 bpb["sec_per_trk"] = json::JsonValue::String(hex::encode_upper(&self.sec_per_trk));
84 bpb["num_heads"] = json::JsonValue::String(hex::encode_upper(&self.num_heads));
85 bpb["hidd_sec"] = json::JsonValue::String(hex::encode_upper(&self.hidd_sec));
86 bpb["tot_sec_32"] = json::JsonValue::String(hex::encode_upper(&self.tot_sec_32));
87 ans["bpb"] = bpb;
88 if let Some(spaces) = indent {
89 return json::stringify_pretty(ans,spaces);
90 } else {
91 return json::stringify(ans);
92 }
93 }
94}
95
96#[derive(DiskStruct)]
98pub struct BPBExtension32 {
99 pub fat_size_32: [u8;4],
101 pub flags: [u8;2],
103 pub fs_version: [u8;2],
105 pub root_cluster: [u8;4],
107 pub fs_info: [u8;2],
111 pub bk_boot_sec: [u8;2],
114 pub reserved: [u8;12]
116}
117
118#[derive(DiskStruct)]
120pub struct BPBTail {
121 pub drv_num: u8,
123 pub reserved1: u8,
125 pub boot_sig: u8,
127 pub vol_id: [u8;4],
129 pub vol_lab: [u8;11],
131 pub fil_sys_type: [u8;8]
134}
135
136#[derive(DiskStruct)]
138pub struct Info {
139 pub lead_sig: [u8;4],
140 pub reserved1: [u8;480],
141 pub struc_sig: [u8;4],
142 pub free_count: [u8;4],
143 pub nxt_free: [u8;4],
144 pub reserved2: [u8;12],
145 pub trail_sig: [u8;4]
146}
147
148impl BPBFoundation {
149 pub fn verify(&self) -> bool {
150 let mut ans = true;
151 let bytes = u16::from_le_bytes(self.bytes_per_sec) as u64;
152 if ![512,1024,2048,4096].contains(&bytes) {
153 debug!("invalid bytes per sector {}",bytes);
154 ans = false;
155 }
156 if ![1,2,4,8,16,32,64,128].contains(&self.sec_per_clus) {
157 debug!("invalid sectors per cluster {}",self.sec_per_clus);
158 ans = false;
159 }
160 if self.reserved_sectors==[0,0] {
161 debug!("invalid count of reserved sectors 0");
162 ans = false;
163 }
164 if self.num_fats==0 {
165 debug!("invalid count of FATs 0");
166 ans = false;
167 }
168 let entries = u16::from_le_bytes(self.root_ent_cnt) as u64;
169 if bytes > 0 && (entries*32)%bytes != 0 {
170 debug!("invalid entry count {}",entries);
171 ans = false;
172 }
173 if self.tot_sec_16==[0,0] && self.tot_sec_32==[0,0,0,0] {
174 debug!("invalid sector count 0");
175 ans = false;
176 }
177 ans
178 }
179 pub fn sec_size(&self) -> u64 {
180 u16::from_le_bytes(self.bytes_per_sec) as u64
181 }
182 pub fn block_size(&self) -> u64 {
183 self.sec_per_clus() as u64 * self.sec_size()
184 }
185 pub fn heads(&self) -> u64 {
186 u16::from_le_bytes(self.num_heads) as u64
187 }
188 pub fn secs_per_track(&self) -> u64 {
189 u16::from_le_bytes(self.sec_per_trk) as u64
190 }
191 pub fn tot_sec(&self) -> u64 {
192 match self.tot_sec_16 {
193 [0,0] => u32::from_le_bytes(self.tot_sec_32) as u64,
194 _ => u16::from_le_bytes(self.tot_sec_16) as u64
195 }
196 }
197 pub fn res_secs(&self) -> u16 {
198 u16::from_le_bytes(self.reserved_sectors)
199 }
200 pub fn root_dir_secs(&self) -> u64 {
201 let bytes = u16::from_le_bytes(self.bytes_per_sec) as u64;
202 let entries = u16::from_le_bytes(self.root_ent_cnt) as u64;
203 if bytes==0 {
204 return u16::MAX as u64;
205 }
206 (entries*32 + bytes - 1) / bytes
207 }
208 pub fn root_dir_entries(&self) -> u64 {
209 u16::from_be_bytes(self.root_ent_cnt) as u64
210 }
211 pub fn sec_per_clus(&self) -> u8 {
212 self.sec_per_clus
215 }
216}
217
218pub struct BootSector {
221 jmp: [u8;3],
222 oem: [u8;8],
223 foundation: BPBFoundation,
224 extension32: BPBExtension32,
227 tail: BPBTail,
228 remainder: Vec<u8>
231}
232
233impl DiskStruct for BootSector {
234 fn new() -> Self where Self: Sized {
235 Self {
236 jmp: [0,0,0],
237 oem: [0;8],
238 foundation: BPBFoundation::new(),
239 extension32: BPBExtension32::new(),
240 tail: BPBTail::new(),
241 remainder: Vec::new()
242 }
243 }
244 fn len(&self) -> usize {
245 13 + self.foundation.len() + self.extension32.len() + self.tail.len()
246 }
247 fn from_bytes(bytes: &[u8]) -> Result<Self,DiskStructError> where Self: Sized {
248 let mut ans = Self::new();
249 ans.update_from_bytes(bytes)?;
250 Ok(ans)
251 }
252 fn update_from_bytes(&mut self,bytes: &[u8]) -> Result<(),DiskStructError> {
253 let tentative = Self {
255 jmp: [0,0,0],
256 oem: [0;8],
257 foundation: BPBFoundation::from_bytes(&bytes[11..36].to_vec())?,
258 extension32: BPBExtension32::from_bytes(&bytes[36..64].to_vec())?,
259 tail: BPBTail::from_bytes(&bytes[64..90].to_vec())?,
260 remainder: bytes[90..].to_vec()
261 };
262 self.jmp = bytes[0..3].try_into().expect(RCH);
265 self.oem = bytes[3..11].try_into().expect(RCH);
266 self.foundation = BPBFoundation::from_bytes(&bytes[11..36].to_vec())?;
267 self.extension32 = BPBExtension32::from_bytes(&bytes[36..64].to_vec())?;
268 self.tail = match tentative.fat_type() {
269 32 => BPBTail::from_bytes(&bytes[64..90].to_vec())?,
270 _ => BPBTail::from_bytes(&bytes[36..62].to_vec())?
271 };
272 self.remainder = match tentative.fat_type() {
273 32 => bytes[90..].to_vec(),
274 _ => bytes[62..].to_vec()
275 };
276 Ok(())
277 }
278 fn to_bytes(&self) -> Vec<u8> {
279 let mut ans: Vec<u8> = Vec::new();
280 match self.fat_type() {
281 32 => {
282 ans.append(&mut self.jmp.to_vec());
283 ans.append(&mut self.oem.to_vec());
284 ans.append(&mut self.foundation.to_bytes());
285 ans.append(&mut self.extension32.to_bytes());
286 ans.append(&mut self.tail.to_bytes());
287 ans.append(&mut self.remainder.clone());
288 },
289 _ => {
290 ans.append(&mut self.jmp.to_vec());
291 ans.append(&mut self.oem.to_vec());
292 ans.append(&mut self.foundation.to_bytes());
293 ans.append(&mut self.tail.to_bytes());
294 ans.append(&mut self.remainder.clone());
295 }
296 }
297 ans
298 }
299}
300
301impl BootSector {
302 pub fn create(kind: &crate::img::DiskKind) -> Result<Self,DYNERR> {
303 use crate::img::names;
304 use crate::img::DiskKind::{D35,D525,D8};
305 match kind {
306 D8(names::CPM_1) => Ok(Self::create1216(SSSD_8)),
307 D8(names::DSDD_77) => Ok(Self::create1216(DSDD_8)),
308 D525(names::IBM_SSDD_8) => Ok(Self::create1216(SSDD_525_8)),
309 D525(names::IBM_SSDD_9) => Ok(Self::create1216(SSDD_525_9)),
310 D525(names::IBM_DSDD_8) => Ok(Self::create1216(DSDD_525_8)),
311 D525(names::IBM_DSDD_9) => Ok(Self::create1216(DSDD_525_9)),
312 D525(names::IBM_DSQD) => Ok(Self::create1216(DSQD_525)),
313 D525(names::IBM_DSHD) => Ok(Self::create1216(DSHD_525)),
314 D35(names::IBM_720) => Ok(Self::create1216(D35_720)),
315 D35(names::IBM_1440) => Ok(Self::create1216(D35_1440)),
316 D35(names::IBM_2880) => Ok(Self::create1216(D35_2880)),
317 _ => Err(Box::new(super::Error::UnsupportedDiskKind))
318 }
319 }
320 fn create1216(bpb: BPBFoundation) -> Self {
321 let tail = BPBTail::new();
322 let sec_size = bpb.sec_size() as usize;
323 let used = JMP_BOOT.len() + OEM_NAME.len() + bpb.len() + tail.len();
324 let mut remainder: Vec<u8> = vec![0;sec_size - used];
325 if sec_size>=512 {
326 remainder[510-used] = BOOT_SIGNATURE[0];
327 remainder[511-used] = BOOT_SIGNATURE[1];
328 }
329 let mut oem = OEM_NAME;
330 oem[5..8].copy_from_slice(&env!("CARGO_PKG_VERSION").as_bytes()[0..3]);
331 Self {
332 jmp: JMP_BOOT,
333 oem,
334 foundation: bpb,
335 extension32: BPBExtension32::new(),
336 tail,
337 remainder
338 }
339 }
340 pub fn replace_foundation(&mut self,kind: &crate::img::DiskKind) -> STDRESULT {
343 let lookup = Self::create(kind)?;
344 self.foundation = lookup.foundation;
345 Ok(())
346 }
347 pub fn verify(sec_data: &Vec<u8>) -> bool {
350 let mut ans = true;
351 if sec_data.len()<512 {
352 debug!("sector too small");
353 return false;
354 }
355 let signature = [sec_data[510],sec_data[511]];
356 if signature!=BOOT_SIGNATURE {
357 debug!("signature mismatch");
358 ans = false;
359 }
360 let bpb = BPBFoundation::from_bytes(&sec_data[11..36].to_vec()).expect(RCH);
361 ans |= bpb.verify();
362 let ext32 = BPBExtension32::from_bytes(&sec_data[36..64].to_vec()).expect(RCH);
363 let fat_secs = match bpb.fat_size_16 {
364 [0,0] => u32::from_le_bytes(ext32.fat_size_32) as u64,
365 _ => u16::from_le_bytes(bpb.fat_size_16) as u64
366 };
367 if fat_secs==0 {
368 debug!("invalid count of FAT sectors 0");
369 ans = false;
370 }
371 if bpb.tot_sec() <= bpb.res_secs() as u64 + (bpb.num_fats as u64 * fat_secs) + bpb.root_dir_secs() {
372 debug!("data region came out 0 or negative");
373 ans = false;
374 }
375 if ans {
376 debug!("BPB counts: {} tot, {} res, {}x{} FAT, {} root",bpb.tot_sec(),fat_secs,bpb.num_fats,bpb.res_secs(),bpb.root_dir_secs());
377 }
378 ans
379 }
380 pub fn label(&self) -> Option<[u8;11]> {
381 if self.tail.boot_sig==0x29 && self.tail.vol_lab!=[0x20;11] {
382 Some(self.tail.vol_lab)
383 } else {
384 None
385 }
386 }
387 pub fn sec_size(&self) -> u64 {
388 self.foundation.sec_size()
389 }
390 pub fn block_size(&self) -> u64 {
391 self.foundation.block_size()
392 }
393 pub fn heads(&self) -> u64 {
394 self.foundation.heads()
395 }
396 pub fn secs_per_track(&self) -> u64 {
397 self.foundation.secs_per_track()
398 }
399 pub fn tot_sec(&self) -> u64 {
400 self.foundation.tot_sec()
401 }
402 pub fn res_secs(&self) -> u16 {
403 self.foundation.res_secs()
404 }
405 pub fn secs_per_clus(&self) -> u8 {
406 self.foundation.sec_per_clus()
407 }
408 pub fn media_byte(&self) -> u8 {
409 self.foundation.media
410 }
411 pub fn root_dir_cluster1(&self) -> u64 {
413 u32::from_le_bytes(self.extension32.root_cluster) as u64
414 }
415 pub fn root_dir_entries(&self) -> u64 {
417 self.foundation.root_dir_entries()
418 }
419 pub fn root_dir_secs(&self) -> u64 {
421 self.foundation.root_dir_secs()
422 }
423 pub fn num_fats(&self) -> u64 {
424 self.foundation.num_fats as u64
425 }
426 pub fn fat_secs(&self) -> u64 {
428 match self.foundation.fat_size_16 {
429 [0,0] => u32::from_le_bytes(self.extension32.fat_size_32) as u64,
430 _ => u16::from_le_bytes(self.foundation.fat_size_16) as u64
431 }
432 }
433 pub fn root_dir_sec_rng(&self) -> [u64;2] {
434 [
435 self.res_secs() as u64 + (self.foundation.num_fats as u64 * self.fat_secs()),
436 self.res_secs() as u64 + (self.foundation.num_fats as u64 * self.fat_secs()) + self.root_dir_secs()
437 ]
438 }
439 pub fn data_rgn_secs(&self) -> u64 {
440 self.tot_sec() - (self.res_secs() as u64 + (self.foundation.num_fats as u64 * self.fat_secs()) + self.root_dir_secs())
441 }
442 pub fn cluster_count_abstract(&self) -> u64 {
445 self.data_rgn_secs()/self.foundation.sec_per_clus() as u64
446 }
447 pub fn cluster_count_usable(&self) -> u64 {
449 let typ = self.fat_type() as u64;
450 u64::min(
451 self.data_rgn_secs()/self.foundation.sec_per_clus() as u64,
452 self.fat_secs() * self.sec_size() * 8 / typ - FIRST_DATA_CLUSTER as u64
453 )
454 }
455 pub fn fat_type(&self) -> usize {
458 match self.cluster_count_abstract() {
459 x if x < 4085 => 12,
460 x if x < 65525 => 16,
461 _ => 32
462 }
463 }
464 pub fn cluster_ref(&self,n: u64) -> [u64;2] {
467 let by_per_sec = u16::from_le_bytes(self.foundation.bytes_per_sec) as u64;
468 match self.fat_type() {
469 12 => {
470 let sec = self.res_secs() as u64 + (n + (n/2))/by_per_sec;
471 let offset = (n + (n/2)) % by_per_sec;
472 [sec,offset]
473 },
474 16 => {
475 let sec = self.res_secs() as u64 + (n*2) / by_per_sec;
476 let offset = (n*2) % by_per_sec;
477 [sec,offset]
478 },
479 32 => {
480 let sec = self.res_secs() as u64 + (n*4) / by_per_sec;
481 let offset = (n*4) % by_per_sec;
482 [sec,offset]
483 }
484 _ => panic!("unexpected FAT type")
485 }
486 }
487 pub fn first_data_sec(&self) -> u64 {
488 self.res_secs() as u64 + self.foundation.num_fats as u64 * self.fat_secs() + self.root_dir_secs()
489 }
490 pub fn first_cluster_sec(&self,n: u64) -> u64 {
491 (n-2)*self.foundation.sec_per_clus() as u64 + self.first_data_sec()
492 }
493 pub fn create_tail(&mut self,drv_num: u8,id: [u8;4],label: [u8;11]) {
494 self.tail.boot_sig = 0x29;
495 self.tail.drv_num = drv_num;
496 self.tail.fil_sys_type = match self.fat_type() {
497 12 => *b"FAT12 ",
498 16 => *b"FAT16 ",
499 32 => *b"FAT32 ",
500 _ => panic!("unexpected FAT type")
501 };
502 self.tail.reserved1 = 0x00;
503 self.tail.vol_id = id;
504 self.tail.vol_lab = label;
505 }
506 pub fn to_json(&self,indent: Option<u16>) -> String {
507 self.foundation.to_json(indent)
508 }
509}
510
511const SSSD_8: BPBFoundation = BPBFoundation {
512 bytes_per_sec: [128,0],
513 sec_per_clus: 4,
514 reserved_sectors: [1,0],
515 num_fats: 2,
516 root_ent_cnt: u16::to_le_bytes(68),
517 tot_sec_16: u16::to_le_bytes(2002),
518 media: 0xfe,
519 fat_size_16: [6,0],
520 sec_per_trk: [26,0],
521 num_heads: [1,0],
522 hidd_sec: [0,0,0,0],
523 tot_sec_32: [0,0,0,0]
524};
525
526const DSDD_8: BPBFoundation = BPBFoundation {
527 bytes_per_sec: u16::to_le_bytes(1024),
528 sec_per_clus: 1,
529 reserved_sectors: [1,0],
530 num_fats: 2,
531 root_ent_cnt: u16::to_le_bytes(192),
532 tot_sec_16: u16::to_le_bytes(1232),
533 media: 0xfe,
534 fat_size_16: [2,0],
535 sec_per_trk: [8,0],
536 num_heads: [2,0],
537 hidd_sec: [0,0,0,0],
538 tot_sec_32: [0,0,0,0]
539};
540
541const SSDD_525_8: BPBFoundation = BPBFoundation {
542 bytes_per_sec: [0,2],
543 sec_per_clus: 1,
544 reserved_sectors: [1,0],
545 num_fats: 2,
546 root_ent_cnt: u16::to_le_bytes(0x40),
547 tot_sec_16: u16::to_le_bytes(320),
548 media: 0xfe,
549 fat_size_16: [1,0],
550 sec_per_trk: [8,0],
551 num_heads: [1,0],
552 hidd_sec: [0,0,0,0],
553 tot_sec_32: [0,0,0,0]
554};
555
556const SSDD_525_9: BPBFoundation = BPBFoundation {
557 bytes_per_sec: [0,2],
558 sec_per_clus: 1,
559 reserved_sectors: [1,0],
560 num_fats: 2,
561 root_ent_cnt: u16::to_le_bytes(0x40),
562 tot_sec_16: u16::to_le_bytes(360),
563 media: 0xfc,
564 fat_size_16: [1,0],
565 sec_per_trk: [9,0],
566 num_heads: [1,0],
567 hidd_sec: [0,0,0,0],
568 tot_sec_32: [0,0,0,0]
569};
570
571const DSDD_525_8: BPBFoundation = BPBFoundation {
572 bytes_per_sec: [0,2],
573 sec_per_clus: 2,
574 reserved_sectors: [1,0],
575 num_fats: 2,
576 root_ent_cnt: u16::to_le_bytes(0x70),
577 tot_sec_16: u16::to_le_bytes(640),
578 media: 0xff,
579 fat_size_16: [1,0],
580 sec_per_trk: [8,0],
581 num_heads: [2,0],
582 hidd_sec: [0,0,0,0],
583 tot_sec_32: [0,0,0,0]
584};
585
586const DSDD_525_9: BPBFoundation = BPBFoundation {
587 bytes_per_sec: [0,2],
588 sec_per_clus: 2,
589 reserved_sectors: [1,0],
590 num_fats: 2,
591 root_ent_cnt: u16::to_le_bytes(0x70),
592 tot_sec_16: u16::to_le_bytes(720),
593 media: 0xfd,
594 fat_size_16: [2,0],
595 sec_per_trk: [9,0],
596 num_heads: [2,0],
597 hidd_sec: [0,0,0,0],
598 tot_sec_32: [0,0,0,0]
599};
600
601const DSQD_525: BPBFoundation = BPBFoundation {
602 bytes_per_sec: [0,2],
603 sec_per_clus: 2,
604 reserved_sectors: [1,0],
605 num_fats: 2,
606 root_ent_cnt: u16::to_le_bytes(0x70),
607 tot_sec_16: u16::to_le_bytes(1280),
608 media: 0xfb,
609 fat_size_16: [2,0],
610 sec_per_trk: [8,0],
611 num_heads: [2,0],
612 hidd_sec: [0,0,0,0],
613 tot_sec_32: [0,0,0,0]
614};
615
616const DSHD_525: BPBFoundation = BPBFoundation {
617 bytes_per_sec: [0,2],
618 sec_per_clus: 1,
619 reserved_sectors: [1,0],
620 num_fats: 2,
621 root_ent_cnt: u16::to_le_bytes(0xe0),
622 tot_sec_16: u16::to_le_bytes(2400),
623 media: 0xf9,
624 fat_size_16: [7,0],
625 sec_per_trk: [15,0],
626 num_heads: [2,0],
627 hidd_sec: [0,0,0,0],
628 tot_sec_32: [0,0,0,0]
629};
630
631const D35_720: BPBFoundation = BPBFoundation {
632 bytes_per_sec: [0,2],
633 sec_per_clus: 2,
634 reserved_sectors: [1,0],
635 num_fats: 2,
636 root_ent_cnt: u16::to_le_bytes(0x70),
637 tot_sec_16: u16::to_le_bytes(1440),
638 media: 0xf9,
639 fat_size_16: [3,0],
640 sec_per_trk: [9,0],
641 num_heads: [2,0],
642 hidd_sec: [0,0,0,0],
643 tot_sec_32: [0,0,0,0]
644};
645
646const D35_1440: BPBFoundation = BPBFoundation {
647 bytes_per_sec: [0,2],
648 sec_per_clus: 1,
649 reserved_sectors: [1,0],
650 num_fats: 2,
651 root_ent_cnt: u16::to_le_bytes(0xe0),
652 tot_sec_16: u16::to_le_bytes(2880),
653 media: 0xf0,
654 fat_size_16: [9,0],
655 sec_per_trk: [18,0],
656 num_heads: [2,0],
657 hidd_sec: [0,0,0,0],
658 tot_sec_32: [0,0,0,0]
659};
660
661const D35_2880: BPBFoundation = BPBFoundation {
662 bytes_per_sec: [0,2],
663 sec_per_clus: 2,
664 reserved_sectors: [1,0],
665 num_fats: 2,
666 root_ent_cnt: u16::to_le_bytes(0xf0),
667 tot_sec_16: u16::to_le_bytes(5760),
668 media: 0xf0,
669 fat_size_16: [9,0],
670 sec_per_trk: [36,0],
671 num_heads: [2,0],
672 hidd_sec: [0,0,0,0],
673 tot_sec_32: [0,0,0,0]
674};