use super::types::*;
use crate::bios::dpb::DiskParameterBlock;
use a2kit_macro::DiskStruct;
use a2kit_macro_derive::DiskStruct;
use log::{debug,warn,trace};
#[derive(DiskStruct,Copy,Clone,PartialEq)]
pub struct Extent {
pub user: u8,
pub name: [u8;8],
pub typ: [u8;3],
idx_low: u8,
last_bytes: u8,
idx_high: u8,
last_records: u8,
pub block_list: [u8;16]
}
#[derive(DiskStruct)]
pub struct Password {
pub user: u8, pub name: [u8;8], pub typ: [u8;3], mode: u8, decoder: u8, pad1: [u8;2],
password: [u8;8],
pad2: [u8;8]
}
#[derive(DiskStruct)]
pub struct Label {
status: u8, pub name: [u8;8],
pub typ: [u8;3],
mode: u8, decoder: u8, pad: [u8;2],
password: [u8;8],
create_time: [u8;4],
mod_time: [u8;4]
}
#[derive(DiskStruct)]
pub struct Timestamp {
status: u8, create1: [u8;4],
mod1: [u8;4],
pass1: u8,
pad1: u8,
create2: [u8;4],
mod2: [u8;4],
pass2: u8,
pad2: u8,
create3: [u8;4],
mod3: [u8;4],
pass3: u8,
pad3: u8,
pad4: u8
}
pub struct Directory {
entries: Vec<[u8;DIR_ENTRY_SIZE]>
}
impl Extent {
pub fn set_name(&mut self,name: [u8;8],typ: [u8;3]) {
for i in 0..8 {
self.name[i] = (name[i] & 0x7f) + (self.name[i] & 0x80);
}
for i in 0..3 {
self.typ[i] = (typ[i] & 0x7f) + (self.typ[i] & 0x80);
}
}
pub fn set_flags(&mut self,name: [u8;8],typ: [u8;3]) {
for i in 0..8 {
self.name[i] = (name[i] & 0x80) + (self.name[i] & 0x7f);
}
for i in 0..3 {
self.typ[i] = (typ[i] & 0x80) + (self.typ[i] & 0x7f);
}
}
pub fn get_flags(&self) -> [u8;11] {
let mut ans: [u8;11] = [0;11];
for i in 0..8 {
ans[i] = self.name[i] & 0x80;
}
for i in 0..3 {
ans[i+8] = self.typ[i] & 0x80;
}
return ans;
}
pub fn get_data_ptr(&self) -> Ptr {
Ptr::ExtentData((self.idx_low as u16 + ((self.idx_high as u16) << 5)) as usize)
}
pub fn set_data_ptr(&mut self,ptr: Ptr) {
match ptr {
Ptr::ExtentData(i) => {
self.idx_low = (i & 0b11111) as u8;
self.idx_high = ((i & 0b11111100000) >> 5) as u8;
},
_ => panic!("wrong pointer type")
}
}
pub fn get_eof(&self) -> usize {
let idx = self.get_data_ptr().unwrap();
let bytes = match self.last_bytes {
0 => RECORD_SIZE as u8,
x => x
};
let mut ans = idx * LOGICAL_EXTENT_SIZE;
ans += match self.last_records {
rc if rc<0x80 => (rc-1) as usize * RECORD_SIZE + bytes as usize,
_ => 0x7f * RECORD_SIZE + bytes as usize
};
return ans;
}
pub fn set_eof(&mut self,x_bytes: usize,vers: [u8;3]) {
let mut total_records = x_bytes/RECORD_SIZE;
self.last_bytes = (x_bytes%RECORD_SIZE) as u8;
if self.last_bytes>0 {
total_records += 1;
}
let recs_per_lx = LOGICAL_EXTENT_SIZE / RECORD_SIZE;
self.last_records = (total_records % recs_per_lx) as u8;
if self.last_records==0 && total_records>0 {
self.last_records = recs_per_lx as u8;
}
if vers[0]<3 {
self.last_bytes = 0;
}
}
pub fn set_block_ptr(&mut self,slot: usize,lx: usize,iblock: u16,dpb: &DiskParameterBlock) {
let lx_per_x = dpb.exm as usize + 1;
match dpb.ptr_size() {
1 => self.block_list[lx*16/lx_per_x + slot] = iblock as u8,
2 => {
self.block_list[2*(lx*8/lx_per_x + slot)] = u16::to_le_bytes(iblock)[0];
self.block_list[2*(lx*8/lx_per_x + slot)+1] = u16::to_le_bytes(iblock)[1];
},
_ => panic!("invalid block pointer size")
}
}
pub fn get_block_list(&self,dpb: &DiskParameterBlock) -> Vec<u16> {
match dpb.ptr_size() {
1 => self.block_list.iter().map(|x| *x as u16).collect::<Vec<u16>>(),
2 => {
let mut ans: Vec<u16> = Vec::new();
for i in 0..8 {
ans.push(u16::from_le_bytes([self.block_list[i*2],self.block_list[i*2+1]]));
}
ans
},
_ => panic!("invalid block pointer size")
}
}
}
impl DiskStruct for Directory {
fn new() -> Self {
let entries: Vec<[u8;DIR_ENTRY_SIZE]> = Vec::new();
Self {
entries
}
}
fn to_bytes(&self) -> Vec<u8> {
let mut ans: Vec<u8> = Vec::new();
for x in &self.entries {
ans.append(&mut x.to_vec());
}
return ans;
}
fn update_from_bytes(&mut self,bytes: &Vec<u8>) {
self.entries = Vec::new();
let num_extents = bytes.len()/DIR_ENTRY_SIZE;
if bytes.len()%DIR_ENTRY_SIZE!=0 {
warn!("directory buffer wrong size");
}
for i in 0..num_extents {
self.entries.push(bytes[i*DIR_ENTRY_SIZE..(i+1)*DIR_ENTRY_SIZE].try_into().expect("bad slice length"));
}
}
fn from_bytes(bytes: &Vec<u8>) -> Self {
let mut ans = Self::new();
ans.update_from_bytes(bytes);
return ans;
}
fn len(&self) -> usize {
return DIR_ENTRY_SIZE*(self.entries.len());
}
}
impl Directory {
pub fn num_entries(&self) -> usize {
self.entries.len()
}
pub fn get_type(&self,ptr: &Ptr) -> ExtentType {
let (idx,xstat) = match ptr {
Ptr::ExtentEntry(i) => (*i,self.entries[*i][0]),
_ => panic!("wrong pointer type")
};
trace!("entry {} has extent type {}",idx,xstat);
match xstat {
x if x<USER_END => ExtentType::File,
x if x==DELETED => ExtentType::Deleted,
x if x<USER_END*2 => ExtentType::Password,
0x20 => ExtentType::Label,
0x21 => ExtentType::Timestamp,
x => {
debug!("unknown extent type {}",x);
ExtentType::Unknown
}
}
}
pub fn get_file(&self,ptr: &Ptr) -> Option<Extent> {
match ptr {
Ptr::ExtentEntry(idx) => match self.entries[*idx][0] {
x if x<USER_END => Some(Extent::from_bytes(&self.entries[*idx].to_vec())),
_ => None
},
_ => panic!("wrong pointer type")
}
}
pub fn set_file(&mut self,ptr: &Ptr,fx: &Extent) {
match ptr {
Ptr::ExtentEntry(idx) => {
self.entries[*idx] = fx.to_bytes().try_into().expect("unexpected size")
},
_ => panic!("wrong pointer type")
}
}
pub fn get_password(&self,ptr: &Ptr) -> Option<Password> {
match ptr {
Ptr::ExtentEntry(idx) => match self.entries[*idx][0] {
x if x>=USER_END && x<USER_END*2 => Some(Password::from_bytes(&self.entries[*idx].to_vec())),
_ => None
},
_ => panic!("wrong pointer type")
}
}
pub fn get_label(&self,ptr: &Ptr) -> Option<Label> {
match ptr {
Ptr::ExtentEntry(idx) => match self.entries[*idx][0] {
0x20 => Some(Label::from_bytes(&self.entries[*idx].to_vec())),
_ => None
},
_ => panic!("wrong pointer type")
}
}
pub fn get_timestamp(&self,ptr: &Ptr) -> Option<Timestamp> {
match ptr {
Ptr::ExtentEntry(idx) => match self.entries[*idx][0] {
0x21 => Some(Timestamp::from_bytes(&self.entries[*idx].to_vec())),
_ => None
},
_ => panic!("wrong pointer type")
}
}
}