use std::io;
use byteorder::{ByteOrder, LE};
use crate::index;
use crate::{Device, Error, Result};
pub struct File<'c> {
device: Device<'c>,
handle: u32,
}
impl<'c> File<'c> {
pub fn open(device: Device<'c>, filename: impl AsRef<[u8]>, flags: u32) -> Result<Self> {
let mut hdl = [0; 4];
device.write_read_exact(index::FILE_OPEN, flags, filename.as_ref(), &mut hdl)?;
Ok(File {
device,
handle: u32::from_le_bytes(hdl),
})
}
pub fn delete(device: Device, filename: impl AsRef<[u8]>, flags: u32) -> Result<()> {
device.write_read(index::FILE_DELETE, flags, filename.as_ref(), &mut []).map(drop)
}
}
pub fn listdir(device: Device, dirname: impl AsRef<[u8]>)
-> Result<Vec<(Vec<u8>, u32, u64)>>
{
let mut files = Vec::new();
let mut buf = [0; 324];
let mut offset = 1;
let mut argument = dirname.as_ref().to_vec();
argument.extend(b"\\*.*");
loop {
match device.write_read_exact(index::FILE_BROWSE, offset, &argument, &mut buf) {
Ok(_) => {
let attrs = LE::read_u32(&buf[4..8]);
let sizeh = LE::read_u32(&buf[32..36]);
let sizel = LE::read_u32(&buf[36..40]);
let size = (sizeh as u64) << 32 | sizel as u64;
let name = buf[48..].iter().copied().take_while(|&b| b != 0).collect();
files.push((name, attrs, size));
}
Err(Error::Ads(_, _, 0x70c)) => return Ok(files),
Err(e) => return Err(e),
}
offset = LE::read_u32(&buf[..4]);
argument.clear();
}
}
impl<'a> io::Write for File<'a> {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.device.write_read(index::FILE_WRITE, self.handle, data, &mut [])
.map_err(map_error)
.map(|_| data.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl<'a> std::io::Read for File<'a> {
fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
self.device.write_read(index::FILE_READ, self.handle, &[], data).map_err(map_error)
}
}
impl<'a> Drop for File<'a> {
fn drop(&mut self) {
let _ = self.device.write_read(index::FILE_CLOSE, self.handle, &[], &mut []);
}
}
fn map_error(e: Error) -> io::Error {
match e {
Error::Io(_, io_error) => io_error,
Error::Ads(_, _, 0x704) => io::ErrorKind::InvalidInput.into(),
Error::Ads(_, _, 0x70C) => io::ErrorKind::NotFound.into(),
_ => io::Error::new(io::ErrorKind::Other, e.to_string()),
}
}
pub const READ: u32 = 1 << 0;
pub const WRITE: u32 = 1 << 1;
pub const APPEND: u32 = 1 << 2;
pub const PLUS: u32 = 1 << 3;
pub const BINARY: u32 = 1 << 4;
pub const TEXT: u32 = 1 << 5;
pub const ENSURE_DIR: u32 = 1 << 6;
pub const ENABLE_DIR: u32 = 1 << 7;
pub const OVERWRITE: u32 = 1 << 8;
pub const OVERWRITE_RENAME: u32 = 1 << 9;
pub const DIRECTORY: u32 = 0x10;