Added minimal drv cursor implementation.

This commit is contained in:
Filipe Rodrigues 2021-05-22 22:22:05 +01:00
parent c0e9786666
commit 7bde99906a
6 changed files with 263 additions and 2 deletions

View File

@ -16,6 +16,7 @@ log = "0.4.13"
# Util
byteorder = "1.4.2"
chrono = "0.4.19"
bit-vec = "0.6.3"
# Derives
thiserror = "1.0.23"

219
dcb-drv/src/cursor.rs Normal file
View File

@ -0,0 +1,219 @@
//! Filesystem cursor
// Modules
pub mod error;
// Exports
pub use error::NewError;
// Imports
use crate::{dir::entry::DirEntryReaderKind, DirEntryReader, DirReader};
use bit_vec::BitVec;
use chrono::NaiveDateTime;
use dcb_util::AsciiStrArr;
use std::{
convert::{TryFrom, TryInto},
io,
};
/// Filesystem cursor
#[derive(PartialEq, Clone, Debug)]
pub struct DrvFsCursor {
/// Root directory
root_dir: DirCursor,
/// All sectors' status
sector_status: BitVec,
}
impl DrvFsCursor {
/// Creates a new filesystem cursor
pub fn new<R: io::Read + io::Seek>(reader: &mut R) -> Result<Self, NewError> {
/// Helper function that sets sector status given a directory
fn iter_file_tree<R: io::Read + io::Seek>(
cursor: &mut R, dir: DirReader, sector_status: &mut BitVec,
) -> Result<DirCursor, NewError> {
// Read all entries
// TODO: Avoid allocations by going through all entries twice.
let entries_reader: Vec<DirEntryReader> = dir
.read_entries(cursor)
.map_err(|err| NewError::ReadDir {
sector_pos: dir.sector_pos(),
err,
})?
.collect::<Result<_, _>>()
.map_err(|err| NewError::ReadDirEntry {
sector_pos: dir.sector_pos(),
err,
})?;
// Set the entries of the directory as filled
let dir_sector = usize::try_from(dir.sector_pos()).expect("Sector position didn't fit into `usize`");
let dir_sectors_len = ((entries_reader.len() + 0x1) * 0x20 + 0x7ff) / 0x800;
for n in 0..dir_sectors_len {
sector_status.set(dir_sector + n, true);
}
// Then add all entries
let mut entries = vec![];
for entry in entries_reader {
let kind = match *entry.kind() {
DirEntryReaderKind::Dir(dir) => {
let dir = iter_file_tree(cursor, dir, sector_status)?;
DirEntryCursorKind::Dir(dir)
},
DirEntryReaderKind::File(file) => {
// Set the file as filled
let file_sector =
usize::try_from(file.sector_pos()).expect("Sector position didn't fit into `usize`");
let file_sectors_len =
usize::try_from((file.size() + 0x7ff) / 0x800).expect("File size didn't fit into `usize`");
for n in 0..file_sectors_len {
sector_status.set(file_sector + n, true);
}
DirEntryCursorKind::File(FileCursor {
extension: *file.extension(),
sector_pos: file.sector_pos(),
size: file.size(),
})
},
};
let entry = DirEntryCursor {
name: *entry.name(),
date: entry.date(),
kind,
};
entries.push(entry);
}
Ok(DirCursor {
sector_pos: dir.sector_pos(),
entries,
})
}
// Go through all directories and files and create a status
let size: usize = reader
.stream_len()
.map_err(NewError::FileSize)?
.try_into()
.expect("File size didn't fit into `usize`");
let mut sector_status = BitVec::from_elem((size + 0x7ff) / 0x800, false);
let root_dir = iter_file_tree(reader, DirReader::new(0), &mut sector_status)?;
Ok(Self {
root_dir,
sector_status,
})
}
/// Returns the root directory
#[must_use]
pub const fn root_dir(&self) -> &DirCursor {
&self.root_dir
}
/// Returns the root directory mutably
#[must_use]
pub fn root_dir_mut(&mut self) -> &mut DirCursor {
&mut self.root_dir
}
}
/// A directory
#[derive(PartialEq, Clone, Debug)]
pub struct DirCursor {
/// Sector position
sector_pos: u32,
/// All entries
entries: Vec<DirEntryCursor>,
}
impl DirCursor {
/// Returns all entries
#[must_use]
pub fn entries(&self) -> &[DirEntryCursor] {
&self.entries
}
}
/// A directory entry cursor
#[derive(PartialEq, Clone, Debug)]
pub struct DirEntryCursor {
/// Entry name
name: AsciiStrArr<0x10>,
/// Entry date
date: NaiveDateTime,
/// Kind
kind: DirEntryCursorKind,
}
impl DirEntryCursor {
/// Get a reference to the dir entry cursor's name.
#[must_use]
pub const fn name(&self) -> &AsciiStrArr<0x10> {
&self.name
}
/// Get a reference to the dir entry cursor's date.
#[must_use]
pub const fn date(&self) -> &NaiveDateTime {
&self.date
}
/// Get a reference to the dir entry cursor's kind.
#[must_use]
pub const fn kind(&self) -> &DirEntryCursorKind {
&self.kind
}
}
/// A directory entry kind
#[derive(PartialEq, Clone, Debug)]
pub enum DirEntryCursorKind {
/// Directory
Dir(DirCursor),
/// File
File(FileCursor),
}
/// A file cursor
#[derive(PartialEq, Clone, Debug)]
pub struct FileCursor {
/// Extension
extension: AsciiStrArr<0x3>,
/// Sector position
sector_pos: u32,
/// Size
size: u32,
}
impl FileCursor {
/// Get a reference to the file cursor's extension.
#[must_use]
pub const fn extension(&self) -> &AsciiStrArr<0x3> {
&self.extension
}
/// Get a reference to the file cursor's sector pos.
#[must_use]
pub const fn sector_pos(&self) -> &u32 {
&self.sector_pos
}
/// Get a reference to the file cursor's size.
#[must_use]
pub const fn size(&self) -> &u32 {
&self.size
}
}

View File

@ -0,0 +1,34 @@
//! Errors
use crate::dir::reader::{ReadEntriesError, ReadEntryError};
use std::io;
/// Error for [`Dir::read_entries`](super::Dir::read_entries)
#[derive(Debug, thiserror::Error)]
pub enum NewError {
/// Unable to get file size
#[error("Unable to get file size")]
FileSize(#[source] io::Error),
/// Unable to read directory
#[error("Unable to read directory at {sector_pos:#x}")]
ReadDir {
/// Position of the sector
sector_pos: u32,
/// Underlying error
#[source]
err: ReadEntriesError,
},
/// Unable to read directory entry
#[error("Unable to read directory entry at {sector_pos:#x}")]
ReadDirEntry {
/// Position of the sector
sector_pos: u32,
/// Underlying error
#[source]
err: ReadEntryError,
},
}

View File

@ -19,6 +19,12 @@ pub struct DirReader {
}
impl DirReader {
/// Creates a directory reader for the root directory
#[must_use]
pub const fn root() -> Self {
Self::new(0)
}
/// Creates a directory reader from it's sector
#[must_use]
pub const fn new(sector_pos: u32) -> Self {

View File

@ -6,7 +6,7 @@ use dcb_util::AsciiStrArr;
use std::io::{self, SeekFrom};
/// A file reader
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct FileReader {
/// File extension
extension: AsciiStrArr<0x3>,

View File

@ -1,6 +1,6 @@
#![doc(include = "lib.md")]
// Features
#![feature(external_doc)]
#![feature(external_doc, seek_stream_len)]
// Lints
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
// We'll disable the ones we don't need
@ -58,6 +58,7 @@
#![allow(clippy::map_err_ignore)]
// Modules
pub mod cursor;
pub mod dir;
pub mod error;
pub mod file;