mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-13 13:22:02 +00:00
Separated entry into two submodules for reader and writer.
This commit is contained in:
@@ -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<Option<Self>, 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<L: DirWriterList> {
|
||||
/// A file
|
||||
File(FileWriter<L::FileReader>),
|
||||
|
||||
/// Directory
|
||||
Dir(DirWriter<L>),
|
||||
}
|
||||
|
||||
/// A directory entry reader
|
||||
#[derive(Debug)]
|
||||
pub struct DirEntryWriter<L: DirWriterList> {
|
||||
/// Entry name
|
||||
name: AsciiStrArr<0x10>,
|
||||
|
||||
/// Entry date
|
||||
date: NaiveDateTime,
|
||||
|
||||
/// Entry kind
|
||||
kind: DirEntryWriterKind<L>,
|
||||
}
|
||||
|
||||
impl<L: DirWriterList> DirEntryWriter<L> {
|
||||
/// Creates a new entry writer from it's name, date and kind
|
||||
pub fn new(name: AsciiStrArr<0x10>, date: NaiveDateTime, kind: DirEntryWriterKind<L>) -> 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<L> {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
/// Returns this entry's kind
|
||||
pub fn into_kind(self) -> DirEntryWriterKind<L> {
|
||||
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};
|
||||
|
||||
104
dcb-io/src/drv/dir/entry/reader.rs
Normal file
104
dcb-io/src/drv/dir/entry/reader.rs
Normal file
@@ -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<Option<Self>, 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
105
dcb-io/src/drv/dir/entry/writer.rs
Normal file
105
dcb-io/src/drv/dir/entry/writer.rs
Normal file
@@ -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<L: DirWriterList> {
|
||||
/// A file
|
||||
File(FileWriter<L::FileReader>),
|
||||
|
||||
/// Directory
|
||||
Dir(DirWriter<L>),
|
||||
}
|
||||
|
||||
/// A directory entry reader
|
||||
#[derive(Debug)]
|
||||
pub struct DirEntryWriter<L: DirWriterList> {
|
||||
/// Entry name
|
||||
name: AsciiStrArr<0x10>,
|
||||
|
||||
/// Entry date
|
||||
date: NaiveDateTime,
|
||||
|
||||
/// Entry kind
|
||||
kind: DirEntryWriterKind<L>,
|
||||
}
|
||||
|
||||
impl<L: DirWriterList> DirEntryWriter<L> {
|
||||
/// Creates a new entry writer from it's name, date and kind
|
||||
pub fn new(name: AsciiStrArr<0x10>, date: NaiveDateTime, kind: DirEntryWriterKind<L>) -> 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<L> {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
/// Returns this entry's kind
|
||||
pub fn into_kind(self) -> DirEntryWriterKind<L> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<E: std::error::Error + 'static> {
|
||||
/// 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),
|
||||
|
||||
Reference in New Issue
Block a user