use crate::boot_sector::bpb::{BiosParameterBlock2, BiosParameterBlock3, BPB_OFFSET};
use crate::io::{Cursor, ReadSeek, ReadWriteSeek, Seek, SeekFrom, Write};
use crate::{DiskImageError, StandardFormat};
use binrw::{binrw, BinRead, BinWrite};
pub struct BootSector {
pub(crate) bpb2: BiosParameterBlock2,
pub(crate) bpb3: BiosParameterBlock3,
pub(crate) marker: [u8; 2],
pub(crate) sector_buf: Cursor<[u8; 512]>,
}
#[binrw]
#[brw(big)]
pub struct CreatorString {
bytes: [u8; 8],
}
impl BootSector {
pub fn new<T: ReadSeek>(buffer: &mut T) -> Result<Self, DiskImageError> {
let mut sector_buf = [0; 512];
buffer.seek(SeekFrom::Start(0))?;
buffer.read_exact(&mut sector_buf)?;
buffer.seek(SeekFrom::Start(BPB_OFFSET))?;
let bpb2 = BiosParameterBlock2::read(buffer)?;
let bpb3 = BiosParameterBlock3::read(buffer)?;
buffer.seek(SeekFrom::End(-2))?;
let mut marker = [0; 2];
buffer.read_exact(&mut marker)?;
Ok(BootSector {
bpb2,
bpb3,
marker,
sector_buf: Cursor::new(sector_buf),
})
}
pub(crate) fn set_creator(&mut self, creator: &[u8; 8]) -> Result<(), DiskImageError> {
let creator_offset = 0x147;
eprintln!(
"Creator offset: {} into {} bytes",
creator_offset,
self.sector_buf.get_ref().len()
);
match self.sector_buf.seek(SeekFrom::Start(creator_offset)) {
Ok(_) => {}
Err(e) => {
eprintln!("Error seeking to creator offset: {:?}", e);
return Err(e)?;
}
}
let creator_string = CreatorString::read(&mut self.sector_buf)?;
if creator_string.bytes != "fluxfox ".as_bytes() {
return Err(DiskImageError::IncompatibleImage);
}
self.sector_buf.seek(SeekFrom::Start(creator_offset))?;
let new_creator_string = CreatorString { bytes: *creator };
new_creator_string.write(&mut self.sector_buf)?;
Ok(())
}
pub fn has_valid_bpb(&self) -> bool {
self.bpb2.is_valid()
}
pub(crate) fn update_bpb_from_format(&mut self, format: StandardFormat) -> Result<(), DiskImageError> {
if format == StandardFormat::Invalid {
return Err(DiskImageError::IncompatibleImage);
}
self.bpb2 = BiosParameterBlock2::from(format);
self.bpb3 = BiosParameterBlock3::from(format);
self.sector_buf.seek(SeekFrom::Start(BPB_OFFSET))?;
self.bpb2.write(&mut self.sector_buf)?;
self.bpb3.write(&mut self.sector_buf)?;
Ok(())
}
pub(crate) fn as_bytes(&self) -> &[u8; 512] {
self.sector_buf.get_ref()
}
pub(crate) fn write_bpb_to_buffer<T: ReadWriteSeek>(&mut self, buffer: &mut T) -> Result<(), DiskImageError> {
buffer.seek(SeekFrom::Start(BPB_OFFSET))?;
self.bpb2.write(buffer)?;
self.bpb3.write(buffer)?;
Ok(())
}
pub(crate) fn get_standard_format(&self) -> Result<StandardFormat, DiskImageError> {
StandardFormat::try_from(&self.bpb2).map_err(|_e| DiskImageError::IncompatibleImage)
}
pub fn dump_bpb<T: Write>(&self, buffer: &mut T) -> Result<(), crate::io::Error> {
writeln!(buffer, "BIOS Parameter Block v2.0:")?;
writeln!(buffer, "\tBytes per sector: {}", self.bpb2.bytes_per_sector)?;
writeln!(buffer, "\tSectors per cluster: {}", self.bpb2.sectors_per_cluster)?;
writeln!(buffer, "\tReserved sectors: {}", self.bpb2.reserved_sectors)?;
writeln!(buffer, "\tNumber of FATs: {}", self.bpb2.number_of_fats)?;
writeln!(buffer, "\tRoot entries: {}", self.bpb2.root_entries)?;
writeln!(buffer, "\tTotal sectors: {}", self.bpb2.total_sectors)?;
writeln!(buffer, "\tMedia descriptor: 0x{:02X}", self.bpb2.media_descriptor)?;
writeln!(buffer, "\tSectors per FAT: {}", self.bpb2.sectors_per_fat)?;
writeln!(buffer)?;
writeln!(buffer, "BIOS Parameter Block v3.0:")?;
writeln!(buffer, "\tSectors per track: {}", self.bpb3.sectors_per_track)?;
writeln!(buffer, "\tNumber of heads: {}", self.bpb3.number_of_heads)?;
writeln!(buffer, "\tHidden sectors: {}", self.bpb3.hidden_sectors)?;
writeln!(buffer)?;
writeln!(
buffer,
"Boot sector marker: 0x{:02X}{:02X}",
self.marker[0], self.marker[1]
)?;
let fmt = self.get_standard_format();
if fmt.is_err() {
writeln!(buffer, "Standard disk format not detected.")?;
} else {
writeln!(
buffer,
"Best standard disk format guess: {:?}",
self.get_standard_format().unwrap()
)?;
}
buffer.flush()?;
Ok(())
}
}