Added dcb_drv::new to eventually replace all of dcb_drv

This commit is contained in:
Filipe Rodrigues 2021-05-25 23:19:51 +01:00
parent ed6c97c4c7
commit 5b36ca95ac
6 changed files with 285 additions and 1 deletions

View File

@ -1,6 +1,6 @@
#![doc(include = "lib.md")]
// Features
#![feature(external_doc, seek_stream_len)]
#![feature(external_doc, seek_stream_len, try_blocks)]
// Lints
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
// We'll disable the ones we don't need
@ -62,6 +62,7 @@ pub mod cursor;
pub mod dir;
pub mod error;
pub mod file;
pub mod new;
// Exports
pub use dir::{DirEntryReader, DirEntryWriter, DirReader, DirWriter, DirWriterLister};

8
dcb-drv/src/new.rs Normal file
View File

@ -0,0 +1,8 @@
//!
// Modules
pub mod entry;
pub mod ptr;
// Exports
pub use entry::{DirEntry, DirEntryKind};

146
dcb-drv/src/new/entry.rs Normal file
View File

@ -0,0 +1,146 @@
//! Directory entry
// Modules
pub mod error;
// Exports
pub use error::FromBytesError;
// Imports
use super::ptr::{DirPtr, FilePtr};
use byteorder::{ByteOrder, LittleEndian};
use chrono::NaiveDateTime;
use dcb_util::{array_split, array_split_mut, ascii_str_arr::AsciiChar, AsciiStrArr};
use std::convert::TryInto;
/// A directory entry kind
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum DirEntryKind {
/// A file
File {
/// Extension
extension: AsciiStrArr<0x3>,
/// Pointer
ptr: FilePtr,
},
/// Directory
Dir {
/// Pointer
ptr: DirPtr,
},
}
/// A directory entry
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct DirEntry {
/// Entry name
pub name: AsciiStrArr<0x10>,
/// Entry date
pub date: NaiveDateTime,
/// Entry kind
pub kind: DirEntryKind,
}
impl DirEntry {
/// Reads a directory entry reader from bytes
pub fn from_bytes(bytes: &[u8; 0x20]) -> Result<Option<Self>, FromBytesError> {
let bytes = array_split!(bytes,
kind : 0x1,
extension : [0x3],
sector_pos: [0x4],
size : [0x4],
date : [0x4],
name : [0x10],
);
// Special case some files which cause problems and return early, as if we encountered the final entry.
#[allow(clippy::single_match)] // We might add more matches in the future
match bytes.name {
[0x83, 0x52, 0x83, 0x53, 0x81, 0x5B, 0x20, 0x81, 0x60, 0x20, 0x43, 0x41, 0x52, 0x44, 0x32, 0x00] => {
log::warn!(
"Ignoring special directory entry: {:?}",
String::from_utf8_lossy(bytes.name)
);
return Ok(None);
},
_ => (),
}
// Then get the name and extension
let mut name = AsciiStrArr::from_bytes(bytes.name).map_err(FromBytesError::Name)?;
name.trim_end(AsciiChar::Null);
let mut extension = AsciiStrArr::from_bytes(bytes.extension).map_err(FromBytesError::Extension)?;
extension.trim_end(AsciiChar::Null);
// Get the sector position, size and date
let sector_pos = LittleEndian::read_u32(bytes.sector_pos);
let size = LittleEndian::read_u32(bytes.size);
let date = NaiveDateTime::from_timestamp(i64::from(LittleEndian::read_u32(bytes.date)), 0);
// Check kind
let kind = match bytes.kind {
0x0 => return Ok(None),
0x1 => DirEntryKind::File {
extension,
ptr: FilePtr { sector_pos, size },
},
0x80 => {
debug_assert_eq!(size, 0, "Directory size wasn't 0");
debug_assert_eq!(extension.len(), 0, "Directory extension wasn't 0");
DirEntryKind::Dir {
ptr: DirPtr { sector_pos },
}
},
&kind => return Err(FromBytesError::InvalidKind(kind)),
};
Ok(Some(Self { name, date, kind }))
}
/// Writes this entry to bytes.
pub fn to_bytes(&self, bytes: &mut [u8; 0x20]) {
let bytes = array_split_mut!(bytes,
kind : 0x1,
extension : [0x3],
sector_pos: [0x4],
size : [0x4],
date : [0x4],
name : [0x10],
);
// Get the kind, extension and size
let (kind, extension, sector_pos, size) = match self.kind {
DirEntryKind::File { extension, ptr } => (0x1, extension, ptr.sector_pos, ptr.size),
DirEntryKind::Dir { ptr } => (0x80, AsciiStrArr::new(), ptr.sector_pos, 0),
};
*bytes.kind = kind;
let extension = extension.as_bytes();
bytes.extension[..extension.len()].copy_from_slice(extension);
bytes.extension[extension.len()..].fill(0);
LittleEndian::write_u32(bytes.size, size);
// Then set the name
let name = self.name.as_bytes();
bytes.name[..name.len()].copy_from_slice(name);
bytes.name[name.len()..].fill(0);
// And the sector
LittleEndian::write_u32(bytes.sector_pos, sector_pos);
// Write the date by saturating it if it's too large or small.
let date = self
.date
.timestamp()
.clamp(0, i64::from(u32::MAX))
.try_into()
.expect("Seconds didn't fit into date");
LittleEndian::write_u32(bytes.date, date);
}
}

View File

@ -0,0 +1,20 @@
//! Errors
// Imports
use dcb_util::ascii_str_arr;
/// Error for [`DirEntry::from_bytes`](super::DirEntry::from_bytes)
#[derive(Debug, thiserror::Error)]
pub enum FromBytesError {
/// Invalid kind
#[error("Invalid kind {_0:#x}")]
InvalidKind(u8),
/// Unable to read name
#[error("Unable to read name")]
Name(#[source] ascii_str_arr::FromBytesError<0x10>),
/// Unable to read extension
#[error("Unable to read extension")]
Extension(#[source] ascii_str_arr::FromBytesError<0x3>),
}

77
dcb-drv/src/new/ptr.rs Normal file
View File

@ -0,0 +1,77 @@
//! Pointers
// Modules
pub mod error;
// Exports
use super::DirEntry;
use dcb_util::IoCursor;
pub use error::{FileCursorError, ReadEntriesError, ReadEntryError};
use std::io::{self, SeekFrom};
/// File pointer
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct FilePtr {
/// Sector position
pub sector_pos: u32,
/// Size
pub size: u32,
}
impl FilePtr {
/// Seeks to this directory on a cursor
pub fn seek_to<T: io::Seek>(self, cursor: &mut T) -> Result<u64, io::Error> {
cursor.seek(SeekFrom::Start(u64::from(self.sector_pos) * 0x800))
}
/// Returns a cursor for this file
pub fn cursor<T: io::Seek>(self, cursor: T) -> Result<IoCursor<T>, FileCursorError> {
let pos = u64::from(self.sector_pos) * 0x800;
IoCursor::new(cursor, pos, u64::from(self.size)).map_err(FileCursorError::Seek)
}
}
/// Directory pointer
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct DirPtr {
/// Sector position
pub sector_pos: u32,
}
impl DirPtr {
/// Returns the root directory pointer
#[must_use]
pub const fn root() -> Self {
Self { sector_pos: 0 }
}
/// Seeks to this directory on a cursor
pub fn seek_to<T: io::Seek>(self, cursor: &mut T) -> Result<u64, io::Error> {
cursor.seek(SeekFrom::Start(u64::from(self.sector_pos) * 0x800))
}
/// Returns an iterator over all entries in this directory
pub fn entries<R: io::Read + io::Seek>(
self, reader: &mut R,
) -> Result<impl Iterator<Item = Result<DirEntry, ReadEntryError>> + '_, ReadEntriesError> {
// Seek to the sector
self.seek_to(reader).map_err(ReadEntriesError::Seek)?;
// Then create the iterator
let iter = std::iter::from_fn(move || {
let entry: Result<_, _> = try {
// Read the bytes
let mut entry_bytes = [0; 0x20];
reader.read_exact(&mut entry_bytes).map_err(ReadEntryError::ReadEntry)?;
// And parse it
DirEntry::from_bytes(&entry_bytes).map_err(ReadEntryError::ParseEntry)?
};
entry.transpose()
});
Ok(iter)
}
}

View File

@ -0,0 +1,32 @@
//! Errors
// Imports
use std::io;
/// Error for [`FilePtr::cursor`](super::FilePtr::cursor)
#[derive(Debug, thiserror::Error)]
pub enum FileCursorError {
/// Unable to seek to file
#[error("Unable to seek to file")]
Seek(#[source] io::Error),
}
/// Error for [`DirPtr::read_entries`](super::DirPtr::read_entries)
#[derive(Debug, thiserror::Error)]
pub enum ReadEntriesError {
/// Unable to seek to directory
#[error("Unable to seek to directory")]
Seek(#[source] io::Error),
}
/// Error for [`DirPtr::read_entries`](super::DirPtr::read_entries)
#[derive(Debug, thiserror::Error)]
pub enum ReadEntryError {
/// Unable to read entry bytes
#[error("Unable to read entry bytes")]
ReadEntry(#[source] io::Error),
/// Unable to parse entry
#[error("Unable to parse entry")]
ParseEntry(#[source] crate::new::entry::FromBytesError),
}