mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-06 17:35:40 +00:00
Started work on separating the iso 9660 filesystem from dcb-io, to leave it just for the custom filesystem.
This commit is contained in:
parent
f80606ec71
commit
087b2ebe87
@ -8,4 +8,5 @@ members = [
|
||||
"dcb-bytes",
|
||||
"dcb-bytes-derive",
|
||||
"dcb-tools",
|
||||
"dcb-iso9660",
|
||||
]
|
||||
|
||||
35
dcb-iso9660/Cargo.toml
Normal file
35
dcb-iso9660/Cargo.toml
Normal 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
56
dcb-iso9660/src/cdrom.rs
Normal 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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
20
dcb-iso9660/src/cdrom/error.rs
Normal file
20
dcb-iso9660/src/cdrom/error.rs
Normal 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),
|
||||
}
|
||||
60
dcb-iso9660/src/cdrom/sector.rs
Normal file
60
dcb-iso9660/src/cdrom/sector.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
53
dcb-iso9660/src/cdrom/sector/address.rs
Normal file
53
dcb-iso9660/src/cdrom/sector/address.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
12
dcb-iso9660/src/cdrom/sector/error.rs
Normal file
12
dcb-iso9660/src/cdrom/sector/error.rs
Normal 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),
|
||||
}
|
||||
75
dcb-iso9660/src/cdrom/sector/header.rs
Normal file
75
dcb-iso9660/src/cdrom/sector/header.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
13
dcb-iso9660/src/cdrom/sector/header/error.rs
Normal file
13
dcb-iso9660/src/cdrom/sector/header/error.rs
Normal 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),
|
||||
}
|
||||
61
dcb-iso9660/src/cdrom/sector/subheader.rs
Normal file
61
dcb-iso9660/src/cdrom/sector/subheader.rs
Normal 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
56
dcb-iso9660/src/lib.rs
Normal 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;
|
||||
Loading…
x
Reference in New Issue
Block a user