mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 00:21:57 +00:00
Removed ByteArray::SIZE.
Added several filesystem entities. Revised alphabet strings.
This commit is contained in:
parent
8b650c5aad
commit
b0921aa93c
@ -9,7 +9,7 @@ where
|
||||
Self: Sized,
|
||||
{
|
||||
/// The type of array required by this structure
|
||||
type ByteArray: ByteArray;
|
||||
type ByteArray: ?Sized + ByteArray;
|
||||
|
||||
/// The error type used for the operation
|
||||
type FromError: Error;
|
||||
@ -25,15 +25,10 @@ where
|
||||
}
|
||||
|
||||
/// A trait for restricting `Bytes::ByteArray`
|
||||
pub trait ByteArray {
|
||||
/// Size of this array
|
||||
const SIZE: usize;
|
||||
}
|
||||
pub trait ByteArray {}
|
||||
|
||||
impl<const N: usize> ByteArray for [u8; N] {
|
||||
const SIZE: usize = N;
|
||||
}
|
||||
impl<const N: usize> ByteArray for [u8; N] {}
|
||||
|
||||
impl ByteArray for u8 {
|
||||
const SIZE: usize = 1;
|
||||
}
|
||||
impl ByteArray for [u8] {}
|
||||
|
||||
impl ByteArray for u8 {}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
//! Game file filesystem
|
||||
//!
|
||||
//! The filesystem used is ISO 9960, which is mostly implemented
|
||||
//! in this module, abstracted from the CD-ROM/XA sectors.
|
||||
//! The filesystem is composed of an outer layer of ISO 9960 with
|
||||
//! a custom file system that may be mounted on some files.
|
||||
|
||||
// Modules
|
||||
pub mod date_time;
|
||||
pub mod dir_record;
|
||||
pub mod error;
|
||||
pub mod string;
|
||||
pub mod volume_descriptor;
|
||||
@ -15,12 +16,13 @@ pub use string::{StrArrA, StrArrD};
|
||||
pub use volume_descriptor::VolumeDescriptor;
|
||||
|
||||
// Imports
|
||||
use self::volume_descriptor::TypeCode;
|
||||
use crate::GameFile;
|
||||
use dcb_bytes::Bytes;
|
||||
use std::io;
|
||||
|
||||
/// The filesystem
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Filesystem {
|
||||
/// Primary volume descriptor
|
||||
primary_volume_descriptor: VolumeDescriptor,
|
||||
@ -30,8 +32,12 @@ impl Filesystem {
|
||||
/// Reads the filesystem from a game file
|
||||
pub fn new<R: io::Read + io::Seek>(file: &mut GameFile<R>) -> Result<Self, NewError> {
|
||||
// Read the primary volume descriptor from sector `0x10`
|
||||
// Note: First `32 kiB` (= 16 sectors) are reserved for arbitrary data.
|
||||
let sector = file.sector(0x10).map_err(NewError::ReadPrimaryVolumeSector)?;
|
||||
let primary_volume_descriptor = VolumeDescriptor::from_bytes(§or.data).map_err(NewError::ParsePrimaryVolume)?;
|
||||
if primary_volume_descriptor.type_code() != TypeCode::Primary {
|
||||
return Err(NewError::FirstVolumeNotPrimary(primary_volume_descriptor.type_code()));
|
||||
}
|
||||
|
||||
Ok(Self { primary_volume_descriptor })
|
||||
}
|
||||
|
||||
87
dcb-io/src/fs/dir_record.rs
Normal file
87
dcb-io/src/fs/dir_record.rs
Normal file
@ -0,0 +1,87 @@
|
||||
//! A directory entry
|
||||
|
||||
// Modules
|
||||
pub mod error;
|
||||
|
||||
// Exports
|
||||
pub use error::FromBytesError;
|
||||
|
||||
// Imports
|
||||
use super::string::FileString;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use dcb_bytes::Bytes;
|
||||
use dcb_util::array_split;
|
||||
use std::convert::TryInto;
|
||||
|
||||
/// A directory record
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct DirRecord {
|
||||
/// Record total size
|
||||
record_size: u8,
|
||||
|
||||
/// File's name
|
||||
name: FileString,
|
||||
|
||||
/// File's location
|
||||
location: u32,
|
||||
|
||||
/// File's size
|
||||
size: u32,
|
||||
}
|
||||
|
||||
impl DirRecord {
|
||||
/// Returns this record's size
|
||||
#[must_use]
|
||||
pub const fn size(&self) -> u8 {
|
||||
self.record_size
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytes for DirRecord {
|
||||
type ByteArray = [u8];
|
||||
type FromError = FromBytesError;
|
||||
type ToError = !;
|
||||
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError> {
|
||||
// Get the header
|
||||
let header_bytes: &[u8; 0x21] = match bytes.get(..0x21).and_then(|bytes| bytes.try_into().ok()) {
|
||||
Some(header_bytes) => header_bytes,
|
||||
None => return Err(FromBytesError::TooSmallHeader),
|
||||
};
|
||||
|
||||
let header_bytes = array_split!(header_bytes,
|
||||
record_size : 0x1,
|
||||
extended_attribute_record_len: 0x1,
|
||||
extent_location_lsb : [0x4],
|
||||
extent_location_msb : [0x4],
|
||||
extent_size_lsb : [0x4],
|
||||
extent_size_msb : [0x4],
|
||||
recording_date_time : [0x7],
|
||||
file_flags : 0x1,
|
||||
file_unit_size : 0x1,
|
||||
interleave_gap_size : 0x1,
|
||||
volume_sequence_number_lsb : [0x2],
|
||||
volume_sequence_number_msb : [0x2],
|
||||
name_len : 0x1,
|
||||
);
|
||||
|
||||
dbg!(bytes);
|
||||
|
||||
// Then read the name
|
||||
let name = bytes
|
||||
.get(0x21..0x21 + usize::from(*header_bytes.name_len))
|
||||
.ok_or(FromBytesError::TooSmallName(*header_bytes.name_len))?;
|
||||
let name = FileString::from_bytes(name).map_err(FromBytesError::Name)?;
|
||||
|
||||
Ok(Self {
|
||||
record_size: *header_bytes.record_size,
|
||||
name,
|
||||
location: LittleEndian::read_u32(header_bytes.extent_location_lsb),
|
||||
size: LittleEndian::read_u32(header_bytes.extent_size_lsb),
|
||||
})
|
||||
}
|
||||
|
||||
fn to_bytes(&self, _bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
20
dcb-io/src/fs/dir_record/error.rs
Normal file
20
dcb-io/src/fs/dir_record/error.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Errors
|
||||
|
||||
// Imports
|
||||
use crate::fs::string;
|
||||
|
||||
/// Error type for [`Bytes::from_bytes`](dcb_bytes::Bytes::from_bytes)
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum FromBytesError {
|
||||
/// Too small
|
||||
#[error("Buffer was too small for header")]
|
||||
TooSmallHeader,
|
||||
|
||||
/// Too small
|
||||
#[error("Buffer was too small for name (expected {_0} for name)")]
|
||||
TooSmallName(u8),
|
||||
|
||||
/// Unable to read name
|
||||
#[error("Unable to read name")]
|
||||
Name(#[source] string::ValidateFileAlphabetError),
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
//! Errors
|
||||
|
||||
// Imports
|
||||
use super::volume_descriptor;
|
||||
use super::volume_descriptor::{self, TypeCode};
|
||||
use crate::game_file::SectorError;
|
||||
|
||||
/// Error type for [`Filesystem::new`](super::Filesystem::new)
|
||||
@ -14,4 +14,8 @@ pub enum NewError {
|
||||
/// Unable to parse primary volume
|
||||
#[error("Unable to parse primary volume")]
|
||||
ParsePrimaryVolume(#[source] volume_descriptor::FromBytesError),
|
||||
|
||||
/// First volume was not the primary volume
|
||||
#[error("First volume was not the primary volume, was {_0:?}")]
|
||||
FirstVolumeNotPrimary(TypeCode),
|
||||
}
|
||||
|
||||
@ -1,109 +1,141 @@
|
||||
//! Filesystem strings
|
||||
|
||||
/// Modules
|
||||
pub mod arr;
|
||||
pub mod error;
|
||||
pub mod owned;
|
||||
pub mod slice;
|
||||
|
||||
// Exports
|
||||
pub use error::InvalidCharError;
|
||||
pub use arr::StrArrAlphabet;
|
||||
pub use error::{InvalidCharError, ValidateFileAlphabetError};
|
||||
pub use owned::StringAlphabet;
|
||||
pub use slice::StrAlphabet;
|
||||
|
||||
// Imports
|
||||
use std::{fmt, marker::PhantomData};
|
||||
|
||||
/// An alphabet
|
||||
/// An alphabet for a string
|
||||
pub trait Alphabet {
|
||||
/// The alphabet
|
||||
fn alphabet() -> &'static [u8];
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Returns if `bytes` are valid for this alphabet
|
||||
fn validate(bytes: &[u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// A alphabetic specific string
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct StrArrAlphabet<A: Alphabet, const N: usize>([u8; N], PhantomData<A>);
|
||||
|
||||
impl<A: Alphabet, const N: usize> StrArrAlphabet<A, N> {
|
||||
/// Parses a string from bytes
|
||||
pub fn from_bytes(bytes: &[u8; N]) -> Result<Self, InvalidCharError> {
|
||||
/// Implements the [`Alphabet`] trait from an alphabet
|
||||
pub trait ImplFromAlphabet {
|
||||
/// The alphabet
|
||||
fn alphabet() -> &'static [u8];
|
||||
|
||||
/// String terminator
|
||||
fn terminator() -> u8;
|
||||
}
|
||||
|
||||
impl<A: ImplFromAlphabet> Alphabet for A {
|
||||
type Error = InvalidCharError;
|
||||
|
||||
fn validate(bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
// If any are invalid, return Err
|
||||
let alphabet = A::alphabet();
|
||||
for (pos, &byte) in bytes.iter().enumerate() {
|
||||
// If we found a space, as long as everything after this position is also a space, it's a valid string
|
||||
if byte == b' ' {
|
||||
match bytes[pos..].iter().all(|&b| b == b' ') {
|
||||
true => break,
|
||||
false => return Err(InvalidCharError { byte, pos }),
|
||||
};
|
||||
// If we found the terminator, terminate
|
||||
// TODO: Maybe make sure everything after the `;` is valid too
|
||||
if byte == Self::terminator() {
|
||||
break;
|
||||
}
|
||||
|
||||
if !alphabet.contains(&byte) {
|
||||
// Else make sure it contains this byte
|
||||
if !Self::alphabet().contains(&byte) {
|
||||
return Err(InvalidCharError { byte, pos });
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self(*bytes, PhantomData))
|
||||
}
|
||||
|
||||
/// Returns the bytes from this string
|
||||
#[must_use]
|
||||
pub fn as_bytes(&self) -> &[u8; N] {
|
||||
&self.0
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet, const N: usize> fmt::Debug for StrArrAlphabet<A, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Try to get self as a string to debug it
|
||||
// TODO: Not allocate here
|
||||
let s = String::from_utf8_lossy(self.as_bytes());
|
||||
|
||||
// Then trim any spaces we might have
|
||||
let s = s.trim();
|
||||
|
||||
write!(f, "{s:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet, const N: usize> fmt::Display for StrArrAlphabet<A, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Try to get self as a string to debug it
|
||||
// TODO: Not allocate here
|
||||
let s = String::from_utf8_lossy(self.as_bytes());
|
||||
|
||||
// Then trim any spaces we might have
|
||||
let s = s.trim();
|
||||
|
||||
write!(f, "{s}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A-type alphabet
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct AlphabetA;
|
||||
|
||||
impl Alphabet for AlphabetA {
|
||||
impl ImplFromAlphabet for AlphabetA {
|
||||
fn alphabet() -> &'static [u8] {
|
||||
&[
|
||||
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
|
||||
b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'_', b' ', b'!', b'"', b'%', b'&', b'\'', b'(', b')',
|
||||
b'*', b'+', b',', b'-', b'.', b'/', b':', b';', b'<', b'=', b'>', b'?',
|
||||
b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'_', b'!', b'"', b'%', b'&', b'\'', b'(', b')', b'*',
|
||||
b'+', b',', b'-', b'.', b'/', b':', b';', b'<', b'=', b'>', b'?',
|
||||
]
|
||||
}
|
||||
|
||||
fn terminator() -> u8 {
|
||||
b' '
|
||||
}
|
||||
}
|
||||
|
||||
/// D-type alphabet
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct AlphabetD;
|
||||
|
||||
impl Alphabet for AlphabetD {
|
||||
impl ImplFromAlphabet for AlphabetD {
|
||||
fn alphabet() -> &'static [u8] {
|
||||
&[
|
||||
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
|
||||
b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'_',
|
||||
]
|
||||
}
|
||||
|
||||
fn terminator() -> u8 {
|
||||
b' '
|
||||
}
|
||||
}
|
||||
|
||||
/// A-type string
|
||||
/// File alphabet
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct FileAlphabet;
|
||||
|
||||
impl Alphabet for FileAlphabet {
|
||||
type Error = ValidateFileAlphabetError;
|
||||
|
||||
fn validate(bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
// Separate into `<name>.<extension>;<version>`
|
||||
let dot_idx = bytes.iter().position(|&b| b == b'.').ok_or(ValidateFileAlphabetError::MissingExtension)?;
|
||||
let version_idx = bytes.iter().position(|&b| b == b';').ok_or(ValidateFileAlphabetError::MissingVersion)?;
|
||||
let (name, bytes) = bytes.split_at(dot_idx);
|
||||
let (extension, version) = bytes.split_at(version_idx);
|
||||
|
||||
// Validate all separately
|
||||
AlphabetD::validate(name).map_err(ValidateFileAlphabetError::InvalidNameChar)?;
|
||||
AlphabetD::validate(extension).map_err(ValidateFileAlphabetError::InvalidExtensionChar)?;
|
||||
match version {
|
||||
[b'0'..=b'9'] => Ok(()),
|
||||
_ => Err(ValidateFileAlphabetError::InvalidVersion),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A-type string array
|
||||
pub type StrArrA<const N: usize> = StrArrAlphabet<AlphabetA, N>;
|
||||
|
||||
/// D-type string
|
||||
/// A-type string
|
||||
pub type StringA = StringAlphabet<AlphabetA>;
|
||||
|
||||
/// A-type string slice
|
||||
pub type StrA = StrAlphabet<AlphabetA>;
|
||||
|
||||
/// D-type string array
|
||||
pub type StrArrD<const N: usize> = StrArrAlphabet<AlphabetD, N>;
|
||||
|
||||
/// D-type string
|
||||
pub type StringD = StringAlphabet<AlphabetD>;
|
||||
|
||||
/// D-type string slice
|
||||
pub type StrD = StrAlphabet<AlphabetD>;
|
||||
|
||||
/// File string array
|
||||
pub type FileStrArr<const N: usize> = StrArrAlphabet<FileAlphabet, N>;
|
||||
|
||||
/// File string
|
||||
pub type FileString = StringAlphabet<FileAlphabet>;
|
||||
|
||||
/// File string slice
|
||||
pub type FileStr = StrAlphabet<FileAlphabet>;
|
||||
|
||||
37
dcb-io/src/fs/string/arr.rs
Normal file
37
dcb-io/src/fs/string/arr.rs
Normal file
@ -0,0 +1,37 @@
|
||||
//! String array
|
||||
|
||||
// Imports
|
||||
use super::{Alphabet, StrAlphabet};
|
||||
use std::{fmt, marker::PhantomData, ops::Deref};
|
||||
|
||||
|
||||
/// A alphabetic specific string array
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct StrArrAlphabet<A: Alphabet, const N: usize>(PhantomData<A>, [u8; N]);
|
||||
|
||||
impl<A: Alphabet, const N: usize> StrArrAlphabet<A, N> {
|
||||
/// Parses a string from bytes
|
||||
pub fn from_bytes(bytes: &[u8; N]) -> Result<Self, A::Error> {
|
||||
A::validate(bytes).map(|()| Self(PhantomData, *bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet, const N: usize> Deref for StrArrAlphabet<A, N> {
|
||||
type Target = StrAlphabet<A>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
ref_cast::RefCast::ref_cast(self.1.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet, const N: usize> fmt::Debug for StrArrAlphabet<A, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", &*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet, const N: usize> fmt::Display for StrArrAlphabet<A, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", &*self)
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
//! Errors
|
||||
|
||||
/// Error for [`StringArrA::from_bytes`] and [`StringArrD::from_bytes`]
|
||||
/// Error for [`Alphabet::validate`](super::Alphabet::validate)'s impl of [`AlphabetA`] and [`AlphabetB`]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Invalid character '{byte:#x}' at index {pos}")]
|
||||
pub struct InvalidCharError {
|
||||
@ -10,3 +10,27 @@ pub struct InvalidCharError {
|
||||
/// Position
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
/// Error for [`Alphabet::validate`](super::Alphabet::validate)'s impl of [`AlphabetA`] and [`AlphabetB`]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ValidateFileAlphabetError {
|
||||
/// Invalid name character
|
||||
#[error("Invalid name character")]
|
||||
InvalidNameChar(#[source] InvalidCharError),
|
||||
|
||||
/// Invalid extension character
|
||||
#[error("Invalid extension character")]
|
||||
InvalidExtensionChar(#[source] InvalidCharError),
|
||||
|
||||
/// Missing file name extension
|
||||
#[error("Missing file name extension")]
|
||||
MissingExtension,
|
||||
|
||||
/// Missing file name version
|
||||
#[error("Missing file name version")]
|
||||
MissingVersion,
|
||||
|
||||
/// Invalid version
|
||||
#[error("Invalid version")]
|
||||
InvalidVersion,
|
||||
}
|
||||
|
||||
36
dcb-io/src/fs/string/owned.rs
Normal file
36
dcb-io/src/fs/string/owned.rs
Normal file
@ -0,0 +1,36 @@
|
||||
//! String
|
||||
|
||||
// Imports
|
||||
use super::{Alphabet, StrAlphabet};
|
||||
use std::{fmt, marker::PhantomData, ops::Deref};
|
||||
|
||||
/// A alphabetic specific string
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct StringAlphabet<A: Alphabet>(PhantomData<A>, Vec<u8>);
|
||||
|
||||
impl<A: Alphabet> StringAlphabet<A> {
|
||||
/// Parses a string from bytes
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, A::Error> {
|
||||
A::validate(bytes).map(|()| Self(PhantomData, bytes.to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet> Deref for StringAlphabet<A> {
|
||||
type Target = StrAlphabet<A>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
ref_cast::RefCast::ref_cast(self.1.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet> fmt::Debug for StringAlphabet<A> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", &*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet> fmt::Display for StringAlphabet<A> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", &*self)
|
||||
}
|
||||
}
|
||||
57
dcb-io/src/fs/string/slice.rs
Normal file
57
dcb-io/src/fs/string/slice.rs
Normal file
@ -0,0 +1,57 @@
|
||||
//! String slice
|
||||
|
||||
// Imports
|
||||
use super::Alphabet;
|
||||
use std::{fmt, marker::PhantomData};
|
||||
|
||||
/// A alphabetic specific string slice
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(ref_cast::RefCast)]
|
||||
#[repr(transparent)]
|
||||
pub struct StrAlphabet<A: Alphabet>(PhantomData<A>, [u8]);
|
||||
|
||||
impl<A: Alphabet> StrAlphabet<A> {
|
||||
/// Returns the bytes from this string
|
||||
#[must_use]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Returns the length of this string
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.as_bytes().len()
|
||||
}
|
||||
|
||||
/// Returns if this string is empty
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet> fmt::Debug for StrAlphabet<A> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Try to get self as a string to debug it
|
||||
// TODO: Not allocate here
|
||||
let s = String::from_utf8_lossy(self.as_bytes());
|
||||
|
||||
// Then trim any spaces we might have
|
||||
let s = s.trim();
|
||||
|
||||
write!(f, "{s:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Alphabet> fmt::Display for StrAlphabet<A> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Try to get self as a string to debug it
|
||||
// TODO: Not allocate here
|
||||
let s = String::from_utf8_lossy(self.as_bytes());
|
||||
|
||||
// Then trim any spaces we might have
|
||||
let s = s.trim();
|
||||
|
||||
write!(f, "{s}")
|
||||
}
|
||||
}
|
||||
@ -9,13 +9,13 @@ pub use error::{FromBytesError, ParseBootRecordError, ParsePrimaryError};
|
||||
pub use type_code::TypeCode;
|
||||
|
||||
// Imports
|
||||
use super::{date_time::DecDateTime, StrArrA, StrArrD};
|
||||
use super::{date_time::DecDateTime, dir_record::DirRecord, StrArrA, StrArrD};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use dcb_bytes::Bytes;
|
||||
use dcb_util::array_split;
|
||||
|
||||
/// A volume descriptor
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum VolumeDescriptor {
|
||||
/// Boot record
|
||||
BootRecord {
|
||||
@ -56,7 +56,7 @@ pub enum VolumeDescriptor {
|
||||
path_table_opt_location: u32,
|
||||
|
||||
/// Root directory entry
|
||||
root_dir_entry: [u8; 0x22],
|
||||
root_dir_entry: DirRecord,
|
||||
|
||||
/// Volume set identifier
|
||||
volume_set_id: StrArrD<0x80>,
|
||||
@ -91,6 +91,21 @@ pub enum VolumeDescriptor {
|
||||
/// Volume effective date time
|
||||
volume_effective_date_time: DecDateTime,
|
||||
},
|
||||
|
||||
/// Set terminator
|
||||
SetTerminator,
|
||||
}
|
||||
|
||||
impl VolumeDescriptor {
|
||||
/// Returns the type code for this descriptor
|
||||
#[must_use]
|
||||
pub const fn type_code(&self) -> TypeCode {
|
||||
match self {
|
||||
Self::BootRecord { .. } => TypeCode::BootRecord,
|
||||
Self::Primary { .. } => TypeCode::Primary,
|
||||
Self::SetTerminator => TypeCode::SetTerminator,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VolumeDescriptor {
|
||||
@ -138,10 +153,10 @@ impl VolumeDescriptor {
|
||||
logical_block_size_msb : [0x2 ],
|
||||
path_table_size_lsb : [0x4 ],
|
||||
path_table_size_msb : [0x4 ],
|
||||
path_table_location_lsb : [0x4 ],
|
||||
path_table_opt_location_lsb : [0x4 ],
|
||||
path_table_location_msb : [0x4 ],
|
||||
path_table_opt_location_msb : [0x4 ],
|
||||
path_table_lsb_location : [0x4 ],
|
||||
path_table_lsb_opt_location : [0x4 ],
|
||||
path_table_msb_location : [0x4 ],
|
||||
path_table_msb_opt_location : [0x4 ],
|
||||
root_dir_entry : [0x22],
|
||||
volume_set_id : [0x80],
|
||||
publisher_id : [0x80],
|
||||
@ -167,9 +182,9 @@ impl VolumeDescriptor {
|
||||
volume_sequence_number: LittleEndian::read_u16(bytes.volume_sequence_number_lsb),
|
||||
logical_block_size: LittleEndian::read_u16(bytes.logical_block_size_lsb),
|
||||
path_table_size: LittleEndian::read_u32(bytes.path_table_size_lsb),
|
||||
path_table_location: LittleEndian::read_u32(bytes.path_table_location_lsb),
|
||||
path_table_opt_location: LittleEndian::read_u32(bytes.path_table_opt_location_lsb),
|
||||
root_dir_entry: *bytes.root_dir_entry,
|
||||
path_table_location: LittleEndian::read_u32(bytes.path_table_lsb_location),
|
||||
path_table_opt_location: LittleEndian::read_u32(bytes.path_table_lsb_opt_location),
|
||||
root_dir_entry: DirRecord::from_bytes(bytes.root_dir_entry).map_err(ParsePrimaryError::RootDirEntry)?,
|
||||
volume_set_id: StrArrD::from_bytes(bytes.volume_set_id).map_err(ParsePrimaryError::VolumeSetId)?,
|
||||
publisher_id: StrArrA::from_bytes(bytes.publisher_id).map_err(ParsePrimaryError::PublisherId)?,
|
||||
data_preparer_id: StrArrA::from_bytes(bytes.data_preparer_id).map_err(ParsePrimaryError::DataPreparerId)?,
|
||||
@ -219,9 +234,8 @@ impl Bytes for VolumeDescriptor {
|
||||
match type_code {
|
||||
TypeCode::BootRecord => Self::parse_boot_record(bytes.descriptor).map_err(FromBytesError::ParseBootRecord),
|
||||
TypeCode::Primary => Self::parse_primary(bytes.descriptor).map_err(FromBytesError::ParsePrimary),
|
||||
TypeCode::Supplementary | TypeCode::VolumePartition | TypeCode::SetTerminator | TypeCode::Reserved(_) => {
|
||||
Err(FromBytesError::TypeCode(type_code))
|
||||
},
|
||||
TypeCode::SetTerminator => Ok(Self::SetTerminator),
|
||||
TypeCode::Supplementary | TypeCode::VolumePartition | TypeCode::Reserved(_) => Err(FromBytesError::TypeCode(type_code)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
// Imports
|
||||
use super::TypeCode;
|
||||
use crate::fs::{date_time, string};
|
||||
use crate::fs::{date_time, dir_record, string};
|
||||
|
||||
/// Error type for [`Bytes::from_bytes`](dcb_bytes::Bytes::from_bytes)
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@ -27,6 +27,7 @@ pub enum FromBytesError {
|
||||
#[error("Unable to parse primary")]
|
||||
ParsePrimary(#[source] ParsePrimaryError),
|
||||
}
|
||||
|
||||
/// Error type for [`VolumeDescriptor::parse_boot_record`](super::VolumeDescriptor::parse_boot_record)
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ParseBootRecordError {
|
||||
@ -50,6 +51,10 @@ pub enum ParsePrimaryError {
|
||||
#[error("Unable to parse volume id")]
|
||||
VolumeId(#[source] string::InvalidCharError),
|
||||
|
||||
/// Unable to parse the root dir entry
|
||||
#[error("Unable to parse the root dir entry")]
|
||||
RootDirEntry(#[source] dir_record::FromBytesError),
|
||||
|
||||
/// Unable to parse volume set id
|
||||
#[error("Unable to parse volume set id")]
|
||||
VolumeSetId(#[source] string::InvalidCharError),
|
||||
|
||||
@ -42,7 +42,7 @@ impl<R: Read + Seek> GameFile<R> {
|
||||
self.reader.seek(SeekFrom::Start(Self::SECTOR_SIZE * n)).map_err(SectorError::Seek)?;
|
||||
|
||||
// Read it
|
||||
let mut bytes = [0u8; <<Sector as Bytes>::ByteArray as dcb_bytes::ByteArray>::SIZE];
|
||||
let mut bytes = [0; 2352];
|
||||
self.reader.read_exact(&mut bytes).map_err(SectorError::Read)?;
|
||||
|
||||
// And parse it
|
||||
|
||||
@ -14,7 +14,9 @@
|
||||
unsafe_block_in_unsafe_fn,
|
||||
never_type,
|
||||
unwrap_infallible,
|
||||
min_const_generics
|
||||
min_const_generics,
|
||||
array_methods,
|
||||
slice_strip
|
||||
)]
|
||||
// Lints
|
||||
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user