From df08fc85a63f33f169c00a0765eb8eb2a98edb1a Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Mon, 25 Jan 2021 04:59:14 +0000 Subject: [PATCH] Separated `entry` into two submodules for reader and writer. --- dcb-io/src/drv/dir/entry.rs | 199 +----------------- dcb-io/src/drv/dir/entry/reader.rs | 104 +++++++++ .../src/drv/dir/entry/{ => reader}/error.rs | 2 +- dcb-io/src/drv/dir/entry/writer.rs | 105 +++++++++ dcb-io/src/drv/dir/error.rs | 4 +- 5 files changed, 216 insertions(+), 198 deletions(-) create mode 100644 dcb-io/src/drv/dir/entry/reader.rs rename dcb-io/src/drv/dir/entry/{ => reader}/error.rs (84%) create mode 100644 dcb-io/src/drv/dir/entry/writer.rs diff --git a/dcb-io/src/drv/dir/entry.rs b/dcb-io/src/drv/dir/entry.rs index 1777d3a..1336535 100644 --- a/dcb-io/src/drv/dir/entry.rs +++ b/dcb-io/src/drv/dir/entry.rs @@ -1,200 +1,9 @@ #![doc(include = "entry.md")] // Modules -pub mod error; +pub mod reader; +pub mod writer; // Exports -pub use error::FromBytesError; - -// Imports -use super::{DirReader, DirWriter, DirWriterList}; -use crate::drv::{FileReader, FileWriter}; -use byteorder::{ByteOrder, LittleEndian}; -use chrono::NaiveDateTime; -use dcb_util::{array_split, array_split_mut, ascii_str_arr::AsciiChar, AsciiStrArr}; -use std::convert::TryFrom; - -/// A directory entry kind -#[derive(PartialEq, Eq, Clone, Debug)] -pub enum DirEntryReaderKind { - /// A file - File(FileReader), - - /// Directory - Dir(DirReader), -} - -/// A directory entry reader -#[derive(PartialEq, Eq, Clone, Debug)] -pub struct DirEntryReader { - /// Entry name - name: AsciiStrArr<0x10>, - - /// Entry date - date: NaiveDateTime, - - /// Entry kind - kind: DirEntryReaderKind, -} - -impl DirEntryReader { - /// Reads a directory entry reader from bytes - pub fn from_bytes(bytes: &[u8; 0x20]) -> Result, FromBytesError> { - let bytes = array_split!(bytes, - kind : 0x1, - extension : [0x3], - sector_pos: [0x4], - size : [0x4], - data : [0x4], - name : [0x10], - ); - - let sector_pos = LittleEndian::read_u32(bytes.sector_pos); - - // Check kind - let kind = match bytes.kind { - 0x0 => return Ok(None), - 0x1 => { - let mut extension = AsciiStrArr::from_bytes(bytes.extension).map_err(FromBytesError::Extension)?; - extension.trim_end(AsciiChar::Null); - let size = LittleEndian::read_u32(bytes.size); - - DirEntryReaderKind::File(FileReader::new(extension, sector_pos, size)) - }, - 0x80 => DirEntryReaderKind::Dir(DirReader::new(sector_pos)), - &kind => return Err(FromBytesError::InvalidKind(kind)), - }; - - // Special case some files which cause problems and return early, as if we encountered the final entry. - // TODO: Generalize this somehow - #[allow(clippy::single_match)] // We'll 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] => { - return Err(FromBytesError::InvalidKind(0)) - }, - _ => (), - } - - // Then get the name and other common metadata - let mut name = AsciiStrArr::from_bytes(bytes.name).map_err(FromBytesError::Name)?; - name.trim_end(AsciiChar::Null); - let date = NaiveDateTime::from_timestamp(i64::from(LittleEndian::read_u32(bytes.data)), 0); - - Ok(Some(Self { name, date, kind })) - } - - /// Returns this entry's name - #[must_use] - pub const fn name(&self) -> &AsciiStrArr<0x10> { - &self.name - } - - /// Returns this entry's date - #[must_use] - pub const fn date(&self) -> NaiveDateTime { - self.date - } - - /// Returns this entry's kind - #[must_use] - pub const fn kind(&self) -> &DirEntryReaderKind { - &self.kind - } -} - -/// A directory entry kind -#[derive(Debug)] -pub enum DirEntryWriterKind { - /// A file - File(FileWriter), - - /// Directory - Dir(DirWriter), -} - -/// A directory entry reader -#[derive(Debug)] -pub struct DirEntryWriter { - /// Entry name - name: AsciiStrArr<0x10>, - - /// Entry date - date: NaiveDateTime, - - /// Entry kind - kind: DirEntryWriterKind, -} - -impl DirEntryWriter { - /// Creates a new entry writer from it's name, date and kind - pub fn new(name: AsciiStrArr<0x10>, date: NaiveDateTime, kind: DirEntryWriterKind) -> Self { - Self { name, date, kind } - } - - /// Returns this entry's size - pub fn size(&self) -> u32 { - match &self.kind { - DirEntryWriterKind::File(file) => file.size(), - DirEntryWriterKind::Dir(dir) => dir.size(), - } - } - - /// Returns this entry's kind - pub fn kind(&self) -> &DirEntryWriterKind { - &self.kind - } - - /// Returns this entry's kind - pub fn into_kind(self) -> DirEntryWriterKind { - self.kind - } - - /// Writes this entry to bytes - pub fn to_bytes(&self, bytes: &mut [u8; 0x20], sector_pos: u32) { - let bytes = array_split_mut!(bytes, - kind : 0x1, - extension : [0x3], - sector_pos: [0x4], - size : [0x4], - data : [0x4], - name : [0x10], - ); - - match &self.kind { - DirEntryWriterKind::File(file) => { - *bytes.kind = 0x1; - - let extension = file.extension().as_bytes(); - bytes.extension[..extension.len()].copy_from_slice(extension); - bytes.extension[extension.len()..].fill(0); - - LittleEndian::write_u32(bytes.size, file.size()); - }, - DirEntryWriterKind::Dir(_) => { - *bytes.kind = 0x80; - - LittleEndian::write_u32(bytes.size, 0); - }, - }; - - // 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 secs = self.date.timestamp(); - let secs = match u32::try_from(secs) { - Ok(secs) => secs, - Err(_) => match secs { - secs if secs < 0 => 0, - secs if secs > i64::from(u32::MAX) => u32::MAX, - _ => unreachable!(), - }, - }; - LittleEndian::write_u32(bytes.data, secs); - } -} +pub use reader::{DirEntryReader, DirEntryReaderKind}; +pub use writer::{DirEntryWriter, DirEntryWriterKind}; diff --git a/dcb-io/src/drv/dir/entry/reader.rs b/dcb-io/src/drv/dir/entry/reader.rs new file mode 100644 index 0000000..c18b45d --- /dev/null +++ b/dcb-io/src/drv/dir/entry/reader.rs @@ -0,0 +1,104 @@ +//! Directory entry reader + +// Modules +pub mod error; + +// Exports +pub use error::FromBytesError; + + +// Imports +use crate::drv::{DirReader, FileReader}; +use byteorder::{ByteOrder, LittleEndian}; +use chrono::NaiveDateTime; +use dcb_util::{array_split, ascii_str_arr::AsciiChar, AsciiStrArr}; + +/// A directory entry kind +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum DirEntryReaderKind { + /// A file + File(FileReader), + + /// Directory + Dir(DirReader), +} + +/// A directory entry reader +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct DirEntryReader { + /// Entry name + name: AsciiStrArr<0x10>, + + /// Entry date + date: NaiveDateTime, + + /// Entry kind + kind: DirEntryReaderKind, +} + +impl DirEntryReader { + /// Reads a directory entry reader from bytes + pub fn from_bytes(bytes: &[u8; 0x20]) -> Result, FromBytesError> { + let bytes = array_split!(bytes, + kind : 0x1, + extension : [0x3], + sector_pos: [0x4], + size : [0x4], + data : [0x4], + name : [0x10], + ); + + // Get the sector position + let sector_pos = LittleEndian::read_u32(bytes.sector_pos); + + // Check kind + let kind = match bytes.kind { + 0x0 => return Ok(None), + 0x1 => { + // Read the extension and file size + let mut extension = AsciiStrArr::from_bytes(bytes.extension).map_err(FromBytesError::Extension)?; + extension.trim_end(AsciiChar::Null); + let size = LittleEndian::read_u32(bytes.size); + + DirEntryReaderKind::File(FileReader::new(extension, sector_pos, size)) + }, + 0x80 => DirEntryReaderKind::Dir(DirReader::new(sector_pos)), + &kind => return Err(FromBytesError::InvalidKind(kind)), + }; + + // 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 entry: {:#x?}", bytes.name); + return Err(FromBytesError::InvalidKind(0)); + }, + _ => (), + } + + // Then get the name and date + let mut name = AsciiStrArr::from_bytes(bytes.name).map_err(FromBytesError::Name)?; + name.trim_end(AsciiChar::Null); + let date = NaiveDateTime::from_timestamp(i64::from(LittleEndian::read_u32(bytes.data)), 0); + + Ok(Some(Self { name, date, kind })) + } + + /// Returns this entry's name + #[must_use] + pub const fn name(&self) -> &AsciiStrArr<0x10> { + &self.name + } + + /// Returns this entry's date + #[must_use] + pub const fn date(&self) -> NaiveDateTime { + self.date + } + + /// Returns this entry's kind + #[must_use] + pub const fn kind(&self) -> &DirEntryReaderKind { + &self.kind + } +} diff --git a/dcb-io/src/drv/dir/entry/error.rs b/dcb-io/src/drv/dir/entry/reader/error.rs similarity index 84% rename from dcb-io/src/drv/dir/entry/error.rs rename to dcb-io/src/drv/dir/entry/reader/error.rs index aba94cd..165d5d7 100644 --- a/dcb-io/src/drv/dir/entry/error.rs +++ b/dcb-io/src/drv/dir/entry/reader/error.rs @@ -3,7 +3,7 @@ // Imports use dcb_util::ascii_str_arr; -/// Error for [`Bytes::from_bytes`](super::Bytes::from_bytes) +/// Error for [`DirEntryReader::from_bytes`](super::DirEntryReader::from_bytes) #[derive(Debug, thiserror::Error)] pub enum FromBytesError { /// Invalid kind diff --git a/dcb-io/src/drv/dir/entry/writer.rs b/dcb-io/src/drv/dir/entry/writer.rs new file mode 100644 index 0000000..09483d5 --- /dev/null +++ b/dcb-io/src/drv/dir/entry/writer.rs @@ -0,0 +1,105 @@ +//! Directory entry writer + +// Imports +use crate::drv::{DirWriter, DirWriterList, FileWriter}; +use byteorder::{ByteOrder, LittleEndian}; +use chrono::NaiveDateTime; +use dcb_util::{array_split_mut, AsciiStrArr}; +use std::convert::TryFrom; + +/// A directory entry kind +#[derive(Debug)] +pub enum DirEntryWriterKind { + /// A file + File(FileWriter), + + /// Directory + Dir(DirWriter), +} + +/// A directory entry reader +#[derive(Debug)] +pub struct DirEntryWriter { + /// Entry name + name: AsciiStrArr<0x10>, + + /// Entry date + date: NaiveDateTime, + + /// Entry kind + kind: DirEntryWriterKind, +} + +impl DirEntryWriter { + /// Creates a new entry writer from it's name, date and kind + pub fn new(name: AsciiStrArr<0x10>, date: NaiveDateTime, kind: DirEntryWriterKind) -> Self { + Self { name, date, kind } + } + + /// Returns this entry's size + pub fn size(&self) -> u32 { + match &self.kind { + DirEntryWriterKind::File(file) => file.size(), + DirEntryWriterKind::Dir(dir) => dir.size(), + } + } + + /// Returns this entry's kind + pub fn kind(&self) -> &DirEntryWriterKind { + &self.kind + } + + /// Returns this entry's kind + pub fn into_kind(self) -> DirEntryWriterKind { + self.kind + } + + /// Writes this entry to bytes + pub fn to_bytes(&self, bytes: &mut [u8; 0x20], sector_pos: u32) { + let bytes = array_split_mut!(bytes, + kind : 0x1, + extension : [0x3], + sector_pos: [0x4], + size : [0x4], + data : [0x4], + name : [0x10], + ); + + match &self.kind { + DirEntryWriterKind::File(file) => { + *bytes.kind = 0x1; + + let extension = file.extension().as_bytes(); + bytes.extension[..extension.len()].copy_from_slice(extension); + bytes.extension[extension.len()..].fill(0); + + LittleEndian::write_u32(bytes.size, file.size()); + }, + DirEntryWriterKind::Dir(_) => { + *bytes.kind = 0x80; + + LittleEndian::write_u32(bytes.size, 0); + }, + }; + + // 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 secs = self.date.timestamp(); + let secs = match u32::try_from(secs) { + Ok(secs) => secs, + Err(_) => match secs { + secs if secs < 0 => 0, + secs if secs > i64::from(u32::MAX) => u32::MAX, + _ => unreachable!(), + }, + }; + LittleEndian::write_u32(bytes.data, secs); + } +} diff --git a/dcb-io/src/drv/dir/error.rs b/dcb-io/src/drv/dir/error.rs index dd51ce5..90704d5 100644 --- a/dcb-io/src/drv/dir/error.rs +++ b/dcb-io/src/drv/dir/error.rs @@ -21,7 +21,7 @@ pub enum ReadEntryError { /// Unable to parse entry #[error("Unable to parse entry")] - ParseEntry(#[source] entry::FromBytesError), + ParseEntry(#[source] entry::reader::FromBytesError), } /// Error for [`DirWriter::to_writer`](super::DirWriter::to_writer) @@ -58,7 +58,7 @@ pub enum WriteEntriesError { /// Unable to seek to entries #[error("Unable to seek to entries")] SeekToEntries(#[source] io::Error), - + /// Unable to write all entries #[error("Unable to write entries")] WriteEntries(#[source] io::Error),