Started work on separating the iso 9660 filesystem from dcb-io, to leave it just for the custom filesystem.

This commit is contained in:
Filipe Rodrigues 2021-01-18 14:28:12 +00:00
parent f80606ec71
commit 087b2ebe87
11 changed files with 442 additions and 0 deletions

View File

@ -8,4 +8,5 @@ members = [
"dcb-bytes",
"dcb-bytes-derive",
"dcb-tools",
"dcb-iso9660",
]

35
dcb-iso9660/Cargo.toml Normal file
View File

@ -0,0 +1,35 @@
[package]
name = "dcb-iso9960"
version = "0.1.0"
authors = ["Filipe Rodrigues <filipejacintorodrigues1@gmail.com>"]
edition = "2018"
[dependencies]
# Dcb
dcb-util = { path = "../dcb-util" }
dcb-bytes = { path = "../dcb-bytes" }
# Log
log = "0.4"
# Util
byteorder = "1.3"
#ascii = { version = "1.0", features = ["serde"] }
#arrayref = "0.3"
#int-conv = "0.1"
#bitmatch = "0.1"
#either = "1.6"
#smallvec = "1.4"
#num_enum = "0.5"
# Serde
#serde = { version = "1.0", features = ["derive"] }
#serde_yaml = "0.8"
# Derives
#derive_more = "0.99"
thiserror = "1.0"
ref-cast = "1.0"
#dcb-bytes-derive = { path = "../dcb-bytes-derive" }

56
dcb-iso9660/src/cdrom.rs Normal file
View File

@ -0,0 +1,56 @@
//! CD-ROM/XA Implementation
//!
//! This module contains the implementation and abstraction
//! of the CD-ROM/XA Mode 2 Form 1 image file format used by
//! the ISO 9660 filesystem.
// Modules
//pub mod error;
//pub mod sector;
// Exports
//pub use error::SectorError;
//pub use sector::Sector;
// Imports
use std::io::{Read, Seek};
/// A CD-ROM/XA Mode 2 Form 1 wrapper
pub struct CdRomReader<R> {
/// Underlying reader
_reader: R,
}
// Constants
impl<R> CdRomReader<R> {
/// Sector size
pub const SECTOR_SIZE: u64 = 2352;
}
// Constructors
impl<R> CdRomReader<R> {
/// Creates a new CD-ROM reader
#[must_use]
pub const fn new(reader: R) -> Self {
Self { _reader: reader }
}
}
// Read
impl<R: Read + Seek> CdRomReader<R> {
/*
/// Reads the `n`th sector
pub fn sector(&mut self, n: u64) -> Result<Sector, SectorError> {
// Seek to the sector.
self.reader.seek(SeekFrom::Start(Self::SECTOR_SIZE * n)).map_err(SectorError::Seek)?;
// Read it
let mut bytes = [0; 2352];
self.reader.read_exact(&mut bytes).map_err(SectorError::Read)?;
// And parse it
let sector = Sector::from_bytes(&bytes).map_err(SectorError::Parse)?;
Ok(sector)
}
*/
}

View File

@ -0,0 +1,20 @@
//! Errors
// Imports
use super::sector;
/// Error type for [`GameFile::sector`](super::GameFile::sector)
#[derive(Debug, thiserror::Error)]
pub enum SectorError {
/// Unable to seek to sector
#[error("Unable to seek to sector")]
Seek(#[source] std::io::Error),
/// Unable to read sector
#[error("Unable to read sector")]
Read(#[source] std::io::Error),
/// Unable to parse sector
#[error("Unable to parse sector")]
Parse(#[source] sector::FromBytesError),
}

View File

@ -0,0 +1,60 @@
//! A game file sector
// Modules
pub mod address;
pub mod error;
pub mod header;
pub mod subheader;
// Exports
pub use address::SectorAddress;
pub use error::FromBytesError;
pub use header::SectorHeader;
pub use subheader::SectorSubHeader;
// Imports
use dcb_bytes::Bytes;
use dcb_util::{array_split, array_split_mut};
/// A game file sector, `0x930` bytes.
pub struct Sector {
/// Header
pub header: SectorHeader,
/// Data
pub data: [u8; 2048],
}
impl Bytes for Sector {
type ByteArray = [u8; 0x930];
type FromError = FromBytesError;
type ToError = !;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError> {
// Split bytes
let bytes = array_split!(bytes,
header: [0x18 ],
data : [0x800],
// TODO: Check errors with sector
_error: [0x118],
);
let header = SectorHeader::from_bytes(bytes.header).map_err(FromBytesError::Header)?;
Ok(Self { header, data: *bytes.data })
}
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> {
// Split bytes
let bytes = array_split_mut!(bytes,
header: [0x18 ],
data : [0x800],
// TODO: Write error correction to this sector
_error: [0x118],
);
self.header.to_bytes(bytes.header).into_ok();
*bytes.data = self.data;
Ok(())
}
}

View File

@ -0,0 +1,53 @@
//! Sector address
// Imports
use dcb_bytes::Bytes;
use dcb_util::{array_split, array_split_mut};
/// The game file's sector address
pub struct SectorAddress {
/// Minutes
min: u8,
/// Seconds
sec: u8,
/// Block
block: u8,
}
impl Bytes for SectorAddress {
type ByteArray = [u8; 0x3];
type FromError = !;
type ToError = !;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError> {
// Split bytes
let bytes = array_split!(bytes,
min : 0x1,
sec : 0x1,
block: 0x1,
);
Ok(Self {
min: *bytes.min,
sec: *bytes.sec,
block: *bytes.block,
})
}
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> {
// Split bytes
let bytes = array_split_mut!(bytes,
min : 0x1,
sec : 0x1,
block: 0x1,
);
*bytes.min = self.min;
*bytes.sec = self.sec;
*bytes.block = self.block;
Ok(())
}
}

View File

@ -0,0 +1,12 @@
//! Errors
// Imports
use super::header;
/// Error type for [`Bytes::from_bytes`](dcb_bytes::Bytes::from_bytes)
#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)]
pub enum FromBytesError {
/// Unable to read header
#[error("Unable to parse header")]
Header(#[source] header::FromBytesError),
}

View File

@ -0,0 +1,75 @@
//! Sector header
// Modules
pub mod error;
// Exports
pub use error::FromBytesError;
// Imports
use super::{SectorAddress, SectorSubHeader};
use dcb_bytes::Bytes;
use dcb_util::{array_split, array_split_mut};
/// The game file's sector header
pub struct SectorHeader {
/// Sector address
pub address: SectorAddress,
/// Subheader
pub subheader: SectorSubHeader,
}
impl SectorHeader {
/// Sync's value
pub const SYNC: [u8; 12] = [0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
}
impl Bytes for SectorHeader {
type ByteArray = [u8; 0x18];
type FromError = FromBytesError;
type ToError = !;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError> {
// Split bytes
let bytes = array_split!(bytes,
sync : [0xc],
address : [0x3],
mode : 0x1 ,
subheader: [0x8],
);
// Check if the sync is correct
if bytes.sync != &Self::SYNC {
return Err(FromBytesError::Sync(*bytes.sync));
}
// If we aren't in mode 2, return
if *bytes.mode != 2 {
return Err(FromBytesError::Mode(*bytes.mode));
}
// Read the address and subheader
let address = SectorAddress::from_bytes(bytes.address).into_ok();
let subheader = SectorSubHeader::from_bytes(bytes.subheader).into_ok();
Ok(Self { address, subheader })
}
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> {
// Split bytes
let bytes = array_split_mut!(bytes,
sync : [0xc],
address : [0x3],
mode : 0x1 ,
subheader: [0x8],
);
*bytes.sync = Self::SYNC;
self.address.to_bytes(bytes.address).into_ok();
*bytes.mode = 2;
self.subheader.to_bytes(bytes.subheader).into_ok();
Ok(())
}
}

View File

@ -0,0 +1,13 @@
//! Errors
/// Error type for [`Bytes::from_bytes`](dcb_bytes::Bytes::from_bytes)
#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)]
pub enum FromBytesError {
/// Sync was wrong
#[error("Sync was wrong, found {_0:?}")]
Sync([u8; 0xc]),
/// Invalid mode
#[error("Invalid mode {_0:?}")]
Mode(u8),
}

View File

@ -0,0 +1,61 @@
//! Sector subheader
// Imports
use byteorder::{ByteOrder, LittleEndian};
use dcb_bytes::Bytes;
use dcb_util::{array_split, array_split_mut};
/// The game file's sector sub-header
pub struct SectorSubHeader {
/// File
pub file: u16,
/// Channel
pub channel: u16,
/// Submode
pub submode: u16,
/// Data type
pub data_type: u16,
}
impl Bytes for SectorSubHeader {
type ByteArray = [u8; 0x8];
type FromError = !;
type ToError = !;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError> {
// Split bytes
let bytes = array_split!(bytes,
file : [0x2],
channel : [0x2],
submode : [0x2],
data_type: [0x2],
);
Ok(Self {
file: LittleEndian::read_u16(bytes.file),
channel: LittleEndian::read_u16(bytes.channel),
submode: LittleEndian::read_u16(bytes.submode),
data_type: LittleEndian::read_u16(bytes.data_type),
})
}
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> {
// Split bytes
let bytes = array_split_mut!(bytes,
file : [0x2],
channel : [0x2],
submode : [0x2],
data_type: [0x2],
);
LittleEndian::write_u16(bytes.file, self.file);
LittleEndian::write_u16(bytes.channel, self.channel);
LittleEndian::write_u16(bytes.submode, self.submode);
LittleEndian::write_u16(bytes.data_type, self.data_type);
Ok(())
}
}

56
dcb-iso9660/src/lib.rs Normal file
View File

@ -0,0 +1,56 @@
//! Dcb Iso 9960 filesystem implementation
//!
//! This crate contains an implementation of the Iso 9960
//! filesystem (ECMA-119) on a CD-ROM/XA Mode 2 Form 1
//! file image format.
// Features
#![feature(never_type, stmt_expr_attributes, unwrap_infallible)]
// Lints
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
// We'll disable the ones we don't need
#![allow(clippy::blanket_clippy_restriction_lints)]
// No unsafe allowed in this crate
#![forbid(unsafe_code)]
// Must use `expect` instead of `unwrap`
#![forbid(clippy::unwrap_used)]
// We don't need to mark every public function `inline`
#![allow(clippy::missing_inline_in_public_items)]
// We prefer literals to be copy-paste-able rather than readable
#![allow(clippy::unreadable_literal)]
// We prefer suffixes to be glued to the literal
#![allow(clippy::unseparated_literal_suffix)]
// We're fine with panicking when entering an unexpected state
#![allow(
clippy::panic,
clippy::unreachable,
clippy::expect_used,
clippy::panic_in_result_fn,
clippy::unwrap_in_result,
clippy::indexing_slicing
)]
// We prefer tail calls
#![allow(clippy::implicit_return)]
// We use multiple implementations to separate logic
#![allow(clippy::multiple_inherent_impl)]
// We use granular error types, usually one for each function, which document the
// errors that might happen, as opposed to documenting them in the function
#![allow(clippy::missing_errors_doc)]
// Due to our module organization, we end up with data types inheriting their module's name
#![allow(clippy::module_name_repetitions)]
// We need arithmetic for this crate
#![allow(clippy::integer_arithmetic, clippy::integer_division)]
// We want to benefit from match ergonomics where possible
#![allow(clippy::pattern_type_mismatch)]
// We only use wildcards when we only care about certain variants
#![allow(clippy::wildcard_enum_match_arm, clippy::match_wildcard_for_single_variants)]
// We're fine with shadowing, as long as it's related
#![allow(clippy::shadow_reuse, clippy::shadow_same)]
// Matching on booleans can look better than `if / else`
#![allow(clippy::match_bool)]
// If the `else` isn't needed, we don't put it
#![allow(clippy::else_if_without_else)]
// Modules
pub mod cdrom;