mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-13 21:23:28 +00:00
Separated entry into two submodules for reader and writer.
This commit is contained in:
@@ -1,200 +1,9 @@
|
|||||||
#![doc(include = "entry.md")]
|
#![doc(include = "entry.md")]
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
pub mod error;
|
pub mod reader;
|
||||||
|
pub mod writer;
|
||||||
|
|
||||||
// Exports
|
// Exports
|
||||||
pub use error::FromBytesError;
|
pub use reader::{DirEntryReader, DirEntryReaderKind};
|
||||||
|
pub use writer::{DirEntryWriter, DirEntryWriterKind};
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
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
|
// Imports
|
||||||
use dcb_util::ascii_str_arr;
|
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)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum FromBytesError {
|
pub enum FromBytesError {
|
||||||
/// Invalid kind
|
/// 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
|
/// Unable to parse entry
|
||||||
#[error("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)
|
/// 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
|
/// Unable to seek to entries
|
||||||
#[error("Unable to seek to entries")]
|
#[error("Unable to seek to entries")]
|
||||||
SeekToEntries(#[source] io::Error),
|
SeekToEntries(#[source] io::Error),
|
||||||
|
|
||||||
/// Unable to write all entries
|
/// Unable to write all entries
|
||||||
#[error("Unable to write entries")]
|
#[error("Unable to write entries")]
|
||||||
WriteEntries(#[source] io::Error),
|
WriteEntries(#[source] io::Error),
|
||||||
|
|||||||
Reference in New Issue
Block a user