mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 00:21:57 +00:00
Added dcb_drv::new to eventually replace all of dcb_drv
This commit is contained in:
parent
ed6c97c4c7
commit
5b36ca95ac
@ -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
8
dcb-drv/src/new.rs
Normal 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
146
dcb-drv/src/new/entry.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
20
dcb-drv/src/new/entry/error.rs
Normal file
20
dcb-drv/src/new/entry/error.rs
Normal 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
77
dcb-drv/src/new/ptr.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
32
dcb-drv/src/new/ptr/error.rs
Normal file
32
dcb-drv/src/new/ptr/error.rs
Normal 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),
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user