Moved psxcdgen files to tools folder

This commit is contained in:
2022-08-21 13:20:53 +02:00
parent 69074600cc
commit 37a7c582e7
41 changed files with 0 additions and 84 deletions

View File

@@ -0,0 +1,11 @@
[package]
name = "cdtypes"
version = "0.5.5"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
byteorder = "*"
paste = "*"
chrono = "*"

View File

@@ -0,0 +1,292 @@
pub mod reader;
pub mod sector;
use sector::*;
use reader::Reader as CDReader;
use super::{Error, types as types};
use types::{cue::{Specifier, DataType, DataTypeEnd}, lsb_msb::ReadWriteEndian, pvd::PrimaryVolumeDescriptor, sector::{Mode2Form1, SubHeaderForm}};
use std::fs::File;
pub struct DirectoryRecordIterator<'a> {
parent: &'a CD,
cur_sector_id: usize,
cur_data: &'a [u8],
is_last: bool
}
impl<'a> DirectoryRecordIterator<'a> {
pub fn new(parent: &CD, sector: usize) -> DirectoryRecordIterator {
let mut new_iter = DirectoryRecordIterator{parent: parent, cur_sector_id: sector, cur_data: &[], is_last: true};
if let Some((data, is_last)) = new_iter.load(sector) {
new_iter.cur_data = data;
new_iter.is_last = is_last;
}
new_iter
}
fn load(&self, sector_id: usize) -> Option<(&'a [u8], bool)> {
if let Some(sector) = self.parent.get_sector(sector_id) {
if let Some(sub_header) = sector.get_sub_header() {
return Some((sector.get_data(), sub_header.sub_mode.is_eof()));
}
else {
return None;
}
}
else {
return None;
}
}
}
impl<'a> std::iter::Iterator for DirectoryRecordIterator<'a> {
type Item = &'a types::dir_record::DirectoryRecord;
fn next(&mut self) -> Option<Self::Item> {
if self.cur_data.is_empty() {
if self.is_last {
None
}
else {
self.cur_sector_id += 1;
if let Some((data, is_last)) = self.load(self.cur_sector_id) {
self.cur_data = data;
self.is_last = is_last;
self.next()
}
else {
None
}
}
}
else {
let mut data_range = self.cur_data.as_ptr_range();
unsafe {
if let Some(cur_dir) = (data_range.start as *const types::dir_record::DirectoryRecord).as_ref() {
let dir_length = cur_dir.length[0] as usize;
if dir_length == 0 {
self.cur_data = &[];
self.next()
}
else {
data_range.start = data_range.start.add(dir_length);
self.cur_data = std::slice::from_raw_parts(data_range.start, data_range.end.offset_from(data_range.start) as usize);
Some(cur_dir)
}
}
else {
None
}
}
}
}
}
pub struct PathTableIterator<'a> {
path_table: Box<[u8]>,
cur_idx: usize,
phantom: std::marker::PhantomData<&'a u8>
}
impl<'a> PathTableIterator<'a> {
pub fn new(cd: &CD, mut sector: usize) -> PathTableIterator {
let mut data = Vec::<u8>::new();
loop {
if let Some(sector) = cd.get_sector(sector) {
if let Some(sub_header) = sector.get_sub_header() {
for byte in sector.get_data() {
data.push(*byte);
}
if sub_header.sub_mode.is_eof() {
return PathTableIterator{path_table: data.into_boxed_slice(), cur_idx: 0usize, phantom: std::marker::PhantomData::default()};
}
}
else {
return PathTableIterator::default();
}
}
else {
return PathTableIterator::default();
}
sector += 1;
}
}
}
impl<'a> std::iter::Iterator for PathTableIterator<'a> {
type Item = &'a types::path_table::PathTableL;
fn next(&mut self) -> Option<Self::Item> {
if (self.cur_idx + std::mem::size_of::<Self::Item>()) < self.path_table.len() {
unsafe {
if let Some(entry) = (self.path_table.as_ptr().add(self.cur_idx) as *const types::path_table::PathTableL).as_ref() {
if entry.name_length[0] > 0 {
self.cur_idx += entry.get_size();
Some(entry)
}
else {
None
}
}
else {
None
}
}
}
else {
None
}
}
}
impl<'a> std::default::Default for PathTableIterator<'a> {
fn default() -> PathTableIterator<'a> {
PathTableIterator{path_table: Box::default(), cur_idx: 0usize, phantom: std::marker::PhantomData::default()}
}
}
pub struct SectorIterator<'a> {
parent: &'a CD,
cur_sec_idx: usize,
}
impl<'a> SectorIterator<'a> {
pub fn new(cd: &'a CD) -> SectorIterator<'a> {
SectorIterator{parent: cd, cur_sec_idx: 0}
}
}
impl<'a> std::iter::Iterator for SectorIterator<'a> {
type Item = &'a Sector;
fn next(&mut self) -> Option<Self::Item> {
let value = self.parent.get_sector(self.cur_sec_idx);
self.cur_sec_idx += 1;
value
}
}
pub struct CD {
sectors: Vec<Sector>,
track_info: Vec<TrackInfo>,
}
impl CD {
pub fn from_file(file: File, specifier: Option<Vec<Specifier>>) -> Result<CD, Error> {
let data_type_ends = {
if let Some(specifier) = specifier {
DataTypeEnd::parse_cue_specifier(specifier)?
}
else {
Vec::new()
}
};
let reader = CDReader::new(file, &data_type_ends)?;
let mut sectors = Vec::new();
for sector in reader {
sectors.push(sector?);
}
Ok(CD{sectors, track_info: TrackInfo::new(data_type_ends)})
}
pub fn sector_count(&self) -> usize {
self.sectors.len()
}
pub fn last_sector_idx(&self) -> usize {
Self::sector_count(self) - 1
}
pub fn get_sector(&self, idx: usize) -> Option<&Sector> {
self.sectors.get(idx)
}
pub fn has_tracks(&self) -> bool {
!self.track_info.is_empty()
}
pub fn get_primary_volume_descriptor(&self) -> Result<&PrimaryVolumeDescriptor, Error> {
if let Some(pvd_sec) = self.get_sector(16) {
if let Sector::CDXAData(pvd_sec) = pvd_sec {
unsafe {
match pvd_sec.sub_header.get_form() {
SubHeaderForm::Form1 => Ok(std::mem::transmute::<&[u8; Mode2Form1::DATA_SIZE], &PrimaryVolumeDescriptor>(&pvd_sec.data)),
_ => Err(Error::GenericError("Sector is not CD-XA Data Form 1".to_string()))
}
}
}
else {
Err(Error::GenericError("Sector is not CD-XA Data".to_string()))
}
}
else {
Err(Error::GenericError("Sector not found".to_string()))
}
}
pub fn directory_record_iter(&self, sector: usize) -> DirectoryRecordIterator {
DirectoryRecordIterator::new(self, sector)
}
pub fn path_table_iter(&self) -> PathTableIterator {
if let Ok(pvd) = self.get_primary_volume_descriptor() {
PathTableIterator::new(self, pvd.path_table_1.read() as usize)
}
else {
PathTableIterator::default()
}
}
pub fn sector_iter(&self) -> SectorIterator {
SectorIterator::new(self)
}
pub fn track_info_iter(&self) -> std::slice::Iter<TrackInfo> {
self.track_info.iter()
}
}
pub struct TrackInfo {
pub r#type: DataType,
pub start: usize,
}
impl TrackInfo {
pub fn new(data_type_ends: Vec<DataTypeEnd>) -> Vec<TrackInfo> {
let mut data = Vec::new();
let mut start = 0;
for data_type_end in data_type_ends {
data.push(TrackInfo{r#type: data_type_end.r#type, start});
start = data_type_end.end;
}
data
}
}

View File

@@ -0,0 +1,114 @@
use super::sector::Sector;
use super::super::{Error, types::{cue::{DataType, DataTypeEnd}, sector::*}};
use std::io::Read;
pub struct Reader<'a> {
file: std::fs::File,
data_type_guide: &'a Vec<DataTypeEnd>,
lba: usize,
active: bool
}
impl<'a> Reader<'a> {
pub fn new(file: std::fs::File, data_type_guide: &'a Vec<DataTypeEnd>) -> Result<Reader, Error> {
Ok(Reader{
file, data_type_guide, lba: 0, active: true
})
}
fn next_data_type(&mut self) -> Option<DataType> {
self.lba += 1;
for data_type in self.data_type_guide.iter() {
if self.lba < data_type.end {
return Some(data_type.r#type.clone());
}
}
None
}
fn process_audio_sector(&mut self) -> Result<Option<Sector>, Error> {
let mut buffer = [0;std::mem::size_of::<Audio>()];
let bytes = self.file.read(&mut buffer)?;
if bytes == 0 {
Ok(None)
}
else {
unsafe {
Ok(Some(Sector::Audio(std::mem::transmute::<[u8; std::mem::size_of::<Audio>()], Audio>(buffer))))
}
}
}
fn process_binary_sector(&mut self) -> Result<Option<Sector>, Error> {
fn make_ok_some(sector: Sector) -> Result<Option<Sector>, Error> {
Ok(Some(sector))
}
let mut buffer = [0;std::mem::size_of::<Mode2Form1>()];
let bytes = self.file.read(&mut buffer)?;
if bytes == 0 {
return Ok(None);
}
unsafe {
let sector = &*(buffer.as_ptr() as *const Mode0);
let mode = sector.header.get_mode();
match mode {
Ok(HeaderMode::Mode0) => make_ok_some(Sector::Empty(std::mem::transmute::<[u8;std::mem::size_of::<Mode0>()], Mode0>(buffer))),
Ok(HeaderMode::Mode1) => make_ok_some(Sector::CDData(std::mem::transmute::<[u8;std::mem::size_of::<Mode1>()], Mode1>(buffer))),
Ok(HeaderMode::Mode2) => {
let sector = &*(buffer.as_ptr() as *const Mode2);
match sector.sub_header.get_form() {
SubHeaderForm::Form1 => make_ok_some(Sector::CDXAData(std::mem::transmute::<[u8;std::mem::size_of::<Mode2Form1>()], Mode2Form1>(buffer))),
SubHeaderForm::Form2 => make_ok_some(Sector::CDXAAudio(std::mem::transmute::<[u8;std::mem::size_of::<Mode2Form2>()], Mode2Form2>(buffer))),
}
}
Err(mode) => {
return Err(Error::TypeError(format!("Unkown Mode: {} @LBA: {}", mode, self.lba - 1)));
}
}
}
}
}
impl<'a> std::iter::Iterator for Reader<'a> {
type Item = Result<Sector, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.active {
let function = {
match self.next_data_type() {
Some(DataType::Audio) => Self::process_audio_sector,
None |
Some(DataType::Mode2_2352) => Self::process_binary_sector,
Some(data_type) => return Some(Err(Error::CUEDataTypeNotSupported(data_type)))
}
};
match function(self) {
Ok(sector) => {
if let Some(sector) = sector {
Some(Ok(sector))
}
else {
None
}
},
Err(error) => {
self.active = false;
return Some(Err(error));
}
}
}
else {
None
}
}
}

View File

@@ -0,0 +1,118 @@
use super::super::{types::{error_correction::*, sector::*}};
#[derive(Clone)]
pub enum Sector {
Audio(Audio),
Empty(Mode0),
CDData(Mode1),
CDXAData(Mode2Form1),
CDXAAudio(Mode2Form2),
}
impl Sector {
pub fn get_header(&self) -> Option<&Header> {
match self {
Sector::Audio(_) => None,
Sector::Empty(sec) => Some(&sec.header),
Sector::CDData(sec) => Some(&sec.header),
Sector::CDXAData(sec) => Some(&sec.header),
Sector::CDXAAudio(sec) => Some(&sec.header),
}
}
pub fn get_sub_header(&self) -> Option<&SubHeader> {
match self {
Sector::CDXAData(sec) => Some(&sec.sub_header),
Sector::CDXAAudio(sec) => Some(&sec.sub_header),
_ => None
}
}
pub fn get_error_correction(&self) -> Option<(&EDC, Option<&ECC>)> {
match self {
Sector::CDData(sector) => Some((&sector.edc, Some(&sector.ecc))),
Sector::CDXAData(sector) => Some((&sector.edc, Some(&sector.ecc))),
Sector::CDXAAudio(sector) => Some((&sector.edc, None)),
_ => None
}
}
pub fn get_data_offset_with_size(&self) -> (usize, usize) {
match self {
Sector::Audio(_) => (0x0, 0x930),
Sector::Empty(_) => (0x10, 0x920),
Sector::CDData(_) => (0x10, 0x800),
Sector::CDXAData(_) => (0x18, 0x800),
Sector::CDXAAudio(_) => (0x18, 0x914)
}
}
pub fn get_friendly_name(&self) -> &str {
match self {
Sector::Audio(_) => "Audio",
Sector::Empty(_) => "Mode0 (Empty)",
Sector::CDData(_) => "Mode1 (Original CDROM)",
Sector::CDXAData(_) => "Mode2/Form1 (CD-XA Data)",
Sector::CDXAAudio(_) => "Mode2/Form2 (CD-XA Audio)"
}
}
pub fn get_data(&self) -> &[u8] {
match self {
Sector::Audio(sector) => sector.get_raw_samples(),
Sector::Empty(sector) => sector.zero.as_slice(),
Sector::CDData(sector) => sector.data.as_slice(),
Sector::CDXAData(sector) => sector.data.as_slice(),
Sector::CDXAAudio(sector) => sector.data.as_slice()
}
}
pub fn get_edc(&self) -> Option<&EDC> {
match self {
Sector::Audio(_) => None,
Sector::Empty(_) => None,
Sector::CDData(sector) => Some(&sector.edc),
Sector::CDXAData(sector) => Some(&sector.edc),
Sector::CDXAAudio(sector) => Some(&sector.edc),
}
}
pub fn get_ecc(&self) -> Option<&ECC> {
match self {
Sector::Audio(_) => None,
Sector::Empty(_) => None,
Sector::CDData(sector) => Some(&sector.ecc),
Sector::CDXAData(sector) => Some(&sector.ecc),
Sector::CDXAAudio(_) => None
}
}
pub fn calculate_edc(&mut self) {
match self {
Sector::Audio(_) => (),
Sector::Empty(_) => (),
Sector::CDData(sector) => {
sector.calculate_edc();
},
Sector::CDXAData(sector) => {
sector.calculate_edc();
},
Sector::CDXAAudio(sector) => {
unsafe {
let raw = std::mem::transmute::<&Mode2Form2, &[u8;std::mem::size_of::<Mode2Form2>()]>(sector);
sector.edc = EDC::calculate_for(&raw[0x10..=0x92B]);
}
}
}
}
pub fn calculate_ecc(&mut self) {
match self {
Sector::Audio(_) => (),
Sector::Empty(_) => (),
Sector::CDData(sector) => sector.calculate_ecc(),
Sector::CDXAData(sector) => sector.calculate_ecc(),
Sector::CDXAAudio(_) => ()
}
}
}

View File

@@ -0,0 +1,45 @@
pub type ByteVector = Vec<u8>;
pub mod cd;
pub mod types;
pub enum Error {
IOError(Option<String>, std::io::Error),
TypeError(String),
GenericError(String),
NoSectorForLBA(usize),
IncorrectCUELine(String),
CUEDataTypeNotSupported(types::cue::DataType),
EOF,
TimeToLarge,
NotImplemented,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::IOError(file_opt, code) => {
if let Some(file) = file_opt {
write!(f, "IO-Error for file \"{}\": {}", file, code)
}
else {
write!(f, "IO-Error: {}", code)
}
},
Error::TypeError(file) => write!(f, "Type error: \"{}\"", file),
Error::GenericError(text) => write!(f, "{}", text),
Error::NoSectorForLBA(lba) => write!(f, "No sector for lba {}", lba),
Error::IncorrectCUELine(line) => write!(f, "Failed parsing \"{}\" as CUE", line),
Error::CUEDataTypeNotSupported(cue_type) => write!(f, "CUE type \"{}\" not supported currently", cue_type),
Error::EOF => write!(f, "Unexpected end of file"),
Error::TimeToLarge => write!(f, "Time exceeded {}min", types::time::Time::MAX_MINUTES),
Error::NotImplemented => write!(f, "<not implemented>"),
}
}
}
impl std::convert::From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Error::IOError(None, error)
}
}

View File

@@ -0,0 +1,54 @@
#[derive(Debug)]
#[derive(Clone)]
pub struct BCDValue {
value: u8
}
impl BCDValue {
pub fn get_decimal(&self) -> u8 {
((self.value >> 4)*10) + (self.value & 0b1111)
}
}
impl std::convert::From<u8> for BCDValue {
fn from(value: u8) -> Self {
BCDValue{value: (((value/10) << 4) | (value%10))}
}
}
impl std::convert::From<BCDValue> for u8 {
fn from(bcd: BCDValue) -> Self {
bcd.get_decimal()
}
}
impl std::convert::From<&BCDValue> for u8 {
fn from(bcd: &BCDValue) -> Self {
bcd.get_decimal()
}
}
#[cfg(test)]
mod bcd_tests {
use super::BCDValue;
#[test]
fn from_one_digit() {
assert_eq!(BCDValue::from(2).value, 0x2);
}
#[test]
fn from_two_digit() {
assert_eq!(BCDValue::from(22).value, 0x22);
}
#[test]
fn one_digit_tou8() {
assert_eq!(u8::from(BCDValue::from(2)), 2);
}
#[test]
fn two_digit_tou8() {
assert_eq!(u8::from(BCDValue::from(22)), 22);
}
}

View File

@@ -0,0 +1,85 @@
use super::super::Error;
pub type AString<const SIZE: usize> = CDString::<AStringValidator, SIZE>;
pub type DString<const SIZE: usize> = CDString::<DStringValidator, SIZE>;
pub type DynAString = CDString<AStringValidator, 0>;
pub type DynDString = CDString<DStringValidator, 0>;
pub trait CDStringValidator {
fn is_valid_ascii(value: &[u8]) -> Result<(), Error>;
}
pub struct AStringValidator {}
pub struct DStringValidator {}
pub struct CDString<F:CDStringValidator, const SIZE: usize> {
value: [u8; SIZE],
_d: std::marker::PhantomData<F>
}
impl CDStringValidator for AStringValidator {
fn is_valid_ascii(value: &[u8]) -> Result<(), Error> {
for chr in value {
let chr = *chr as char;
if !matches!(chr, '0'..='9' | 'A'..='Z' | ' ' | '!' | '"' | '%' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | '=' | '>' | '?' | '_') {
return Err(Error::GenericError(format!("{} not a valid a-character", chr)));
}
}
Ok(())
}
}
impl CDStringValidator for DStringValidator {
fn is_valid_ascii(value: &[u8]) -> Result<(), Error> {
for chr in value {
let chr = *chr as char;
if !matches!(chr, '0'..='9' | 'A'..='Z' | '_') {
return Err(Error::GenericError(format!("{} not a valid d-character", chr)));
}
}
Ok(())
}
}
impl<F:CDStringValidator, const SIZE: usize> CDString<F, SIZE> {
pub fn from_str(string: &str) -> Result<CDString<F, SIZE>, Error> {
let mut cd_str = CDString::default();
CDString::<F, 0>::to_buffer(string, &mut cd_str.value)?;
Ok(cd_str)
}
pub fn as_raw(&self) -> &[u8] {
&self.value
}
}
impl<F:CDStringValidator> CDString<F, 0> {
pub fn to_buffer(string: &str, buffer: &mut [u8]) -> Result<(), Error> {
if !string.is_ascii() {
return Err(Error::GenericError("String has to be ASCII encoded".to_string()));
}
if string.len() > buffer.len() {
return Err(Error::GenericError(format!("String has {} characters but buffer can only hold {}", string.len(), buffer.len())));
}
F::is_valid_ascii(string.as_bytes())?;
let bytes = string.as_bytes();
for i in 0..bytes.len() {
buffer[i] = bytes[i];
}
Ok(())
}
}
impl<F:CDStringValidator, const SIZE: usize> Default for CDString<F, SIZE> {
fn default() -> Self {
CDString{value: [' ' as u8; SIZE], _d: std::marker::PhantomData}
}
}

View File

@@ -0,0 +1,204 @@
pub mod writer;
pub mod read;
use super::time::Time;
use super::super::Error;
use std::ops::Add;
pub enum Specifier {
File{path: String, format: Format},
Track{number: u64, data_type: DataType},
Index{number: u64, time: Time},
PreGap{time: Time},
}
impl Specifier {
const FILE_IDENTIFIER:&'static str = "FILE";
const TRACK_IDENTIFIER:&'static str = "TRACK";
const INDEX_IDENTIFIER:&'static str = "INDEX";
const PREGAP_IDENTIFIER:&'static str = "PREGAP";
pub fn parse_line(string: &str) -> Option<Specifier> {
let mut slices = string.split_whitespace();
match slices.next() {
Some(Self::FILE_IDENTIFIER) => Self::parse_file(slices),
Some(Self::TRACK_IDENTIFIER) => Self::parse_track(slices),
Some(Self::INDEX_IDENTIFIER) => Self::parse_index(slices),
Some(Self::PREGAP_IDENTIFIER) => Self::parse_pregap(slices),
_ => None,
}
}
fn parse_file(mut slices: std::str::SplitWhitespace) -> Option<Specifier> {
let mut path = slices.next()?.to_owned();
if !path.starts_with('"') {
return None;
}
path.remove(0);
while !path.ends_with('"') {
path = path.add(slices.next()?);
}
path.pop();
Some(Specifier::File{path, format: Format::parse(slices.next()?)?})
}
fn parse_track(mut slices: std::str::SplitWhitespace) -> Option<Specifier> {
let number = slices.next()?;
let data_type = slices.next()?;
Some(Specifier::Track{number: number.parse().ok()?, data_type: DataType::parse(data_type)?})
}
fn parse_index(mut slices: std::str::SplitWhitespace) -> Option<Specifier> {
let number = slices.next()?;
let time = slices.next()?;
Some(Specifier::Index{number: number.parse().ok()?, time: Time::parse(time)?})
}
fn parse_pregap(mut slices: std::str::SplitWhitespace) -> Option<Specifier> {
let time = slices.next()?;
Some(Specifier::PreGap{time: Time::parse(time)?})
}
}
impl std::fmt::Display for Specifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::File{path, format} => write!(f, "{} \"{}\" {}", Self::FILE_IDENTIFIER, path, format),
Self::Track{number, data_type} => write!(f, "{} {:02} {}", Self::TRACK_IDENTIFIER, number, data_type),
Self::Index{number, time} => write!(f, "{} {:02} {}", Self::INDEX_IDENTIFIER, number, time),
Self::PreGap{time} => write!(f, "{} {}", Self::PREGAP_IDENTIFIER, time),
}
}
}
pub enum Format {
Binary
}
impl Format {
const BINARY_IDENTIFIER:&'static str = "BINARY";
pub fn parse(string: &str) -> Option<Format> {
match string {
Self::BINARY_IDENTIFIER => Some(Format::Binary),
_ => None
}
}
}
impl std::fmt::Display for Format {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Binary => write!(f, "{}", Self::BINARY_IDENTIFIER)
}
}
}
#[derive(Clone)]
pub enum DataType {
Audio,
Cdg,
Mode1_2048,
Mode1_2353,
Mode2_2336,
Mode2_2352,
Cdi2336,
Cdi2352
}
impl DataType {
const AUDIO_IDENTIFIER:&'static str = "AUDIO";
const CDG_IDENTIFIER:&'static str = "CDG";
const MODE1_2048_IDENTIFIER:&'static str = "MODE1/2048";
const MODE1_2353_IDENTIFIER:&'static str = "MODE1/2353";
const MODE2_2336_IDENTIFIER:&'static str = "MODE2/2336";
const MODE2_2352_IDENTIFIER:&'static str = "MODE2/2352";
const CDI_2336_IDENTIFIER:&'static str = "CDI/2336";
const CDI_2352_IDENTIFIER:&'static str = "CDI/2352";
pub fn parse(string: &str) -> Option<DataType> {
match string {
Self::AUDIO_IDENTIFIER => Some(Self::Audio),
Self::CDG_IDENTIFIER => Some(Self::Cdg),
Self::MODE1_2048_IDENTIFIER => Some(Self::Mode1_2048),
Self::MODE1_2353_IDENTIFIER => Some(Self::Mode1_2353),
Self::MODE2_2336_IDENTIFIER => Some(Self::Mode2_2336),
Self::MODE2_2352_IDENTIFIER => Some(Self::Mode2_2352),
Self::CDI_2336_IDENTIFIER => Some(Self::Cdi2336),
Self::CDI_2352_IDENTIFIER => Some(Self::Cdi2352),
_ => None
}
}
}
impl std::fmt::Display for DataType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Audio => f.pad(Self::AUDIO_IDENTIFIER),
Self::Cdg => f.pad(Self::CDG_IDENTIFIER),
Self::Mode1_2048 => f.pad(Self::MODE1_2048_IDENTIFIER),
Self::Mode1_2353 => f.pad(Self::MODE1_2353_IDENTIFIER),
Self::Mode2_2336 => f.pad(Self::MODE2_2336_IDENTIFIER),
Self::Mode2_2352 => f.pad(Self::MODE2_2352_IDENTIFIER),
Self::Cdi2336 => f.pad(Self::CDI_2336_IDENTIFIER),
Self::Cdi2352 => f.pad(Self::CDI_2352_IDENTIFIER)
}
}
}
pub struct DataTypeEnd {
pub r#type: DataType,
pub end: usize
}
impl DataTypeEnd {
fn new_infinite_entry(r#type: DataType) -> DataTypeEnd {
DataTypeEnd{r#type, end: usize::MAX}
}
fn is_infinite_entry(&self) -> bool {
self.end == usize::MAX
}
pub fn parse_cue_specifier(cue_specifier: Vec<Specifier>) -> Result<Vec<DataTypeEnd>, Error> {
fn get_prev_if_infinite(parsed_cue: &mut Vec<DataTypeEnd>) -> Option<&mut DataTypeEnd> {
let cur_size = parsed_cue.len();
let element = parsed_cue.get_mut(cur_size - 2)?;
if element.is_infinite_entry() {
Some(element)
}
else {
None
}
}
let mut parsed_cue = Vec::new();
for specifier in cue_specifier {
match specifier {
Specifier::Track{number: _, data_type} => {
match data_type {
DataType::Audio | DataType::Mode2_2352 => parsed_cue.push(DataTypeEnd::new_infinite_entry(data_type)),
_ => return Err(Error::CUEDataTypeNotSupported(data_type)),
}
}
Specifier::Index{number:_, time} => {
if let Some(prev_entry) = get_prev_if_infinite(&mut parsed_cue) {
prev_entry.end = time.to_lba();
}
},
_ => (),
}
}
Ok(parsed_cue)
}
}

View File

@@ -0,0 +1,20 @@
use std::{fs::File, io::{BufRead, BufReader}};
use super::{{super::super::Error}, *};
pub fn read(file: File) -> Result<Vec<Specifier>, Error> {
let mut specifiers = Vec::new();
let file_buffer = BufReader::new(file);
for line in file_buffer.lines() {
let line = line?;
if let Some(specifier) = Specifier::parse_line(line.as_ref()) {
specifiers.push(specifier);
}
else {
return Err(Error::IncorrectCUELine(line));
}
}
Ok(specifiers)
}

View File

@@ -0,0 +1,25 @@
use std::{fs::File, io::Write};
use super::{{super::super::Error}, *};
pub fn write(mut file: File, data: Vec<Specifier>) -> Result<(), Error> {
fn write_indent(file: &mut File, specifier: &Specifier) -> Result<(), Error> {
let indent = {
match specifier {
Specifier::File{..} => 0,
Specifier::Track{..} => 2,
_ => 4,
}
};
write!(file, "{:indent$}", " ")?;
Ok(())
}
for specifier in data {
write_indent(&mut file, &specifier)?;
write!(file, "{}\n", specifier)?;
}
Ok(())
}

View File

@@ -0,0 +1,293 @@
use chrono::{Duration, FixedOffset, TimeZone as ChronoTimeZone, prelude::*};
trait DateTimeGetter {
fn get_datetime(&self) -> Option<DateTime<FixedOffset>> {
if self.get_month() == 0 || self.get_day() == 0 {
None
}
else {
Some(FixedOffset::east(self.get_timezone_sec()).ymd(self.get_year(), self.get_month(), self.get_day()).and_hms_milli(self.get_hour(), self.get_minute(), self.get_second(), self.get_milli_second()))
}
}
fn get_year(&self) -> i32;
fn get_month(&self) -> u32;
fn get_day(&self) -> u32;
fn get_hour(&self) -> u32;
fn get_minute(&self) -> u32;
fn get_second(&self) -> u32;
fn get_milli_second(&self) -> u32;
fn get_timezone_sec(&self) -> i32;
}
#[repr(packed(1))]
pub struct Date {
year: [u8; 4],
month: [u8; 2],
day: [u8; 2],
hour: [u8; 2],
minute: [u8; 2],
second: [u8; 2],
ten_ms: [u8; 2],
pub time_zone: TimeZone
}
#[repr(packed(1))]
pub struct SmallDate {
year: u8,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
pub time_zone: TimeZone
}
#[repr(packed(1))]
pub struct TimeZone {
//Can be between -48 (GMT-12) and 52 (GMT+13)
value: i8
}
impl std::fmt::Display for dyn DateTimeGetter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.get_datetime() {
Some(datetime) => write!(f, "{}", datetime),
None => write!(f, "<Empty>"),
}
}
}
impl Date {
pub const SECS_PER_HOUR:i32 = 3600;
pub fn new() -> Date {
Date{
year: ['0' as u8;4],
month: ['0' as u8;2],
day: ['0' as u8;2],
hour: ['0' as u8;2],
minute: ['0' as u8;2],
second: ['0' as u8;2],
ten_ms: ['0' as u8;2],
time_zone: TimeZone::new()
}
}
pub fn now() -> Date {
let utc = Local::now();
let mut date = Date::new();
date.set_year(utc.year() as u32);
date.set_month(utc.month());
date.set_day(utc.day());
date.set_hour(utc.hour());
date.set_minute(utc.minute());
date.set_second(utc.second());
date.set_milliseconds((utc.timestamp_millis()%1000) as u32);
date.time_zone.set_from_date_time(&utc);
date
}
pub fn set_year(&mut self, year: u32) {
self.year = Self::set_value(year);
}
pub fn set_month(&mut self, month: u32) {
self.month = Self::set_value(month);
}
pub fn set_day(&mut self, day: u32) {
self.day = Self::set_value(day);
}
pub fn set_hour(&mut self, hour: u32) {
self.hour = Self::set_value(hour);
}
pub fn set_minute(&mut self, minute: u32) {
self.minute = Self::set_value(minute);
}
pub fn set_second(&mut self, second: u32) {
self.second = Self::set_value(second);
}
pub fn set_milliseconds(&mut self, milliseconds: u32) {
self.ten_ms = Self::set_value(milliseconds/10);
}
fn set_value<const SIZE:usize>(mut number: u32) -> [u8;SIZE] {
let mut value = ['0' as u8;SIZE];
for i in 0..SIZE {
value[(SIZE - 1 - i)] = ('0' as u32 + (number%10)) as u8;
number /= 10;
}
value
}
fn get_value<const SIZE:usize>(data: &[u8; SIZE]) -> u32 {
let mut number = 0u32;
for i in (SIZE - 1)..=0 {
number += (data[i] - '0' as u8) as u32;
number *= 10;
}
number
}
}
impl std::fmt::Display for Date {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(self as &dyn DateTimeGetter).fmt(f)
}
}
impl DateTimeGetter for Date {
fn get_year(&self) -> i32 {
Self::get_value(&self.year) as i32
}
fn get_month(&self) -> u32 {
Self::get_value(&self.month)
}
fn get_day(&self) -> u32 {
Self::get_value(&self.day)
}
fn get_hour(&self) -> u32 {
Self::get_value(&self.hour)
}
fn get_minute(&self) -> u32 {
Self::get_value(&self.minute)
}
fn get_second(&self) -> u32 {
Self::get_value(&self.second)
}
fn get_milli_second(&self) -> u32 {
Self::get_value(&self.ten_ms)*10
}
fn get_timezone_sec(&self) -> i32 {
self.time_zone.get_gmt_offset()*15*60
}
}
impl SmallDate {
pub fn now() -> SmallDate {
let utc = Local::now();
let mut date = SmallDate::default();
date.set_year(utc.year() as u32);
date.set_month(utc.month());
date.set_day(utc.day());
date.set_hour(utc.hour());
date.set_minute(utc.minute());
date.set_second(utc.second());
date.time_zone.set_from_date_time(&utc);
date
}
pub fn set_year(&mut self, year: u32) {
self.year = (year - 1900) as u8;
}
pub fn set_month(&mut self, month: u32) {
self.month = month as u8;
}
pub fn set_day(&mut self, day: u32) {
self.day = day as u8;
}
pub fn set_hour(&mut self, hour: u32) {
self.hour = hour as u8;
}
pub fn set_minute(&mut self, minute: u32) {
self.minute = minute as u8;
}
pub fn set_second(&mut self, second: u32) {
self.second = second as u8;
}
}
impl std::fmt::Display for SmallDate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(self as &dyn DateTimeGetter).fmt(f)
}
}
impl std::default::Default for SmallDate {
fn default() -> SmallDate {
SmallDate{year: 0u8, month: 0u8, day: 0u8, hour: 0u8, minute: 0u8, second: 0u8, time_zone: TimeZone::new()}
}
}
impl DateTimeGetter for SmallDate {
fn get_year(&self) -> i32 {
(self.year as i32) + 1900
}
fn get_month(&self) -> u32 {
self.month as u32
}
fn get_day(&self) -> u32 {
self.day as u32
}
fn get_hour(&self) -> u32 {
self.hour as u32
}
fn get_minute(&self) -> u32 {
self.minute as u32
}
fn get_second(&self) -> u32 {
self.second as u32
}
fn get_milli_second(&self) -> u32 {
0
}
fn get_timezone_sec(&self) -> i32 {
self.time_zone.get_gmt_offset()*15*60
}
}
impl TimeZone {
pub fn new() -> TimeZone {
TimeZone{value: 0}
}
pub fn set_from_date_time<Tz: chrono::TimeZone>(&mut self, time: &chrono::DateTime::<Tz>) {
const SECS_PER_MINUTE:i32 = 60;
const MIN_PER_QUARTER_HOUR:i32 = 15;
self.value = (time.offset().fix().local_minus_utc()/SECS_PER_MINUTE/MIN_PER_QUARTER_HOUR) as i8;
}
pub fn get_gmt_offset(&self) -> i32 {
self.value as i32
}
pub fn get_hour_correction(&self) -> Duration {
Duration::hours((self.value/4) as i64)
}
}

View File

@@ -0,0 +1,259 @@
use super::{date::SmallDate, helper::{force_convert_ascii_to_str}, lsb_msb::{ReadWriteEndian, BigEndianU16, LittleBigEndianU32, LittleBigEndianU16}};
use crate::read_write_bit_getter_setter;
use std::concat;
#[repr(packed(1))]
pub struct DirectoryRecord {
pub length: [u8; 1],
pub ex_attribute_record_length: [u8; 1],
pub data_block_number: LittleBigEndianU32,
pub data_size: LittleBigEndianU32,
pub time_stamp: SmallDate,
pub flags: [u8; 1],
pub unit_size: [u8; 1],
pub interleave_gap_size: [u8; 1],
pub volume_sequence_number: LittleBigEndianU16,
pub name_length: [u8; 1],
//name: [u8; name_length]
//padding: [u8; if name is even this is 0x00 otherwise not present]
//system_use: <any system use>
}
#[repr(packed(1))]
pub struct CDXASystemUse {
pub owner_id_group: BigEndianU16,
pub owner_id_user: BigEndianU16,
pub file_attribute: FileAttribute,
pub signature: [u8; 2],
pub file_number: [u8; 1],
_reserved: [u8; 5],
}
#[repr(packed(1))]
pub struct FileAttribute {
value: BigEndianU16
}
impl DirectoryRecord {
pub unsafe fn new(&mut self, name: &str, has_system_use: bool) {
let length = Self::calculate_size_for(name, has_system_use);
*self = DirectoryRecord{
length: [length as u8],
ex_attribute_record_length: [0u8],
data_block_number: LittleBigEndianU32::default(),
data_size: LittleBigEndianU32::default(),
time_stamp: SmallDate::default(),
flags: [0u8],
unit_size: [0u8],
interleave_gap_size: [0u8],
volume_sequence_number: LittleBigEndianU16::new(1),
name_length: [name.len() as u8],
};
self.set_name(name);
if has_system_use {
if let Some(system_use) = self.get_cdxa_system_use_mut() {
*system_use = CDXASystemUse::default();
}
}
}
fn get_system_use_offset(name_length: u8, length: u8) -> Option<(usize, usize)> {
let offset = std::mem::size_of::<Self>() + name_length as usize + Self::padding_field_size(name_length as usize);
let len_su = length as usize - offset;
if len_su == 0 {
None
}
else {
Some((offset, len_su))
}
}
pub fn get_system_use_raw(&self) -> Option<&[u8]> {
unsafe {
let (offset, len_su) = Self::get_system_use_offset(self.name_length[0], self.length[0])?;
let raw_system_use:*const u8 = (&self.length as *const u8).add(offset);
Some(std::slice::from_raw_parts(raw_system_use, len_su))
}
}
pub fn get_system_use_raw_mut(&mut self) -> Option<&mut [u8]> {
unsafe {
let (offset, len_su) = Self::get_system_use_offset(self.name_length[0], self.length[0])?;
let raw_system_use:*mut u8 = (&mut self.length as *mut u8).add(offset);
Some(std::slice::from_raw_parts_mut(raw_system_use, len_su))
}
}
pub fn get_cdxa_system_use(&self) -> Option<&CDXASystemUse> {
let raw = self.get_system_use_raw()?;
if let Ok(raw) = raw.try_into() {
unsafe {
Some(std::mem::transmute::<&[u8;14], &CDXASystemUse>(raw))
}
}
else {
None
}
}
pub fn get_cdxa_system_use_mut(&mut self) -> Option<&mut CDXASystemUse> {
let raw = self.get_system_use_raw_mut()?;
if let Ok(raw) = raw.try_into() {
unsafe {
Some(std::mem::transmute::<&mut [u8;14], &mut CDXASystemUse>(raw))
}
}
else {
None
}
}
pub fn set_name(&mut self, name: &str) {
unsafe {
let mut raw_str:*mut u8 = (&mut self.name_length as *mut u8).add(1);
for char in name.as_bytes() {
*raw_str = *char;
raw_str = raw_str.add(1);
}
}
}
pub fn get_name(&self) -> &str {
unsafe {
let raw_str:*const u8 = (&self.name_length as *const u8).add(1);
force_convert_ascii_to_str(std::slice::from_raw_parts(raw_str, self.name_length[0] as usize))
}
}
pub fn set_file(&mut self) {
self.flags[0] = 0;
}
pub fn set_directory(&mut self) {
self.flags[0] = 2;
}
pub fn is_file(&self) -> bool {
self.flags[0] == 0
}
pub fn is_directory(&self) -> bool {
self.flags[0] == 2
}
pub fn calculate_size_for(name: &str, has_system_use: bool) -> usize {
std::mem::size_of::<Self>() + name.len() + Self::padding_field_size(name.len()) + {
if has_system_use {
std::mem::size_of::<CDXASystemUse>()
}
else {
0usize
}
}
}
fn padding_field_size(length: usize) -> usize {
if (length & 0x1) == 0 {
1
}
else {
0
}
}
}
impl std::fmt::Display for DirectoryRecord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\tData Block Number: {}\n", self.data_block_number.read_any())?;
write!(f, "\tData Size Bytes: {}\n", self.data_size.read_any())?;
write!(f, "\tTime Stamp: {}\n", self.time_stamp)?;
write!(f, "\tFile Flags: {}\n", self.flags[0])?;
write!(f, "\tFile Unit Size: {}\n", self.unit_size[0])?;
write!(f, "\tInterleaved Gap Size: {}\n", self.interleave_gap_size[0])?;
write!(f, "\tVolume Sequence Number: {}\n", self.volume_sequence_number.read_any())?;
write!(f, "\tName: {}\n", self.get_name())?;
if let Some(system_use) = self.get_cdxa_system_use() {
write!(f, "System Use Area:\n{}", system_use)
}
else {
write!(f, "\t<No System Use Area>")
}
}
}
impl std::default::Default for CDXASystemUse {
fn default() -> CDXASystemUse {
CDXASystemUse{
owner_id_group: BigEndianU16::new(0),
owner_id_user: BigEndianU16::new(0),
file_attribute: FileAttribute::default(),
signature: ['X' as u8, 'A' as u8],
file_number: [0u8],
_reserved: [0u8; 5],
}
}
}
impl std::fmt::Display for CDXASystemUse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, concat!(
"\t\towner id group: {}\n",
"\t\towner id user: {}\n",
"\t\tfile attribute:\n{}",
"\t\tsignature: {}\n",
"\t\tfile number: {}"),
self.owner_id_group.read(), self.owner_id_user.read(), self.file_attribute, force_convert_ascii_to_str(&self.signature), self.file_number[0])
}
}
impl FileAttribute {
read_write_bit_getter_setter!(value, 0, owner_read);
read_write_bit_getter_setter!(value, 2, owner_execute);
read_write_bit_getter_setter!(value, 4, group_read);
read_write_bit_getter_setter!(value, 6, group_execute);
read_write_bit_getter_setter!(value, 8, world_read);
read_write_bit_getter_setter!(value, 10, world_execute);
read_write_bit_getter_setter!(value, 11, mode2);
read_write_bit_getter_setter!(value, 12, mode2_form2);
read_write_bit_getter_setter!(value, 13, interleaved);
read_write_bit_getter_setter!(value, 14, cdda);
read_write_bit_getter_setter!(value, 15, directory);
}
impl std::default::Default for FileAttribute {
fn default() -> FileAttribute {
let mut att = FileAttribute{value: BigEndianU16::new(0)};
att.set_owner_read();
att.set_owner_execute();
att.set_group_read();
att.set_group_execute();
att.set_world_read();
att.set_world_execute();
att
}
}
impl std::fmt::Display for FileAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\t\t\towner_read: {}, owner_execute: {}\n", self.is_owner_read(), self.is_owner_execute())?;
write!(f, "\t\t\tgroup_read: {}, group_execute: {}\n", self.is_group_read(), self.is_group_execute())?;
write!(f, "\t\t\tworld_read: {}, world_execute: {}\n", self.is_world_read(), self.is_world_execute())?;
write!(f, "\t\t\tmode2: {}, mode2_form2: {}\n", self.is_mode2(), self.is_mode2_form2())?;
write!(f, "\t\t\tinterleaved: {}, CDDA: {}\n", self.is_interleaved(), self.is_cdda())?;
write!(f, "\t\t\tdirectory: {}\n", self.is_directory())
}
}

View File

@@ -0,0 +1,120 @@
pub mod tables;
use super::{sector::SECTOR_SIZE, lsb_msb::{ReadWriteEndian, LittleEndianU32}};
type RawSector = [u8; SECTOR_SIZE];
#[repr(packed(1))]
#[derive(Clone, Default)]
pub struct EDC {
data: LittleEndianU32
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct ECC {
data: [u8; 276]
}
impl EDC {
pub fn calculate_for(data: &[u8]) -> EDC {
let mut edc = EDC{data: LittleEndianU32::default()};
edc.calculate(data);
edc
}
pub fn calculate(&mut self, data: &[u8]) {
let mut edc = 0u32;
for byte in data {
edc = edc ^ *byte as u32;
edc = (edc >> 8) ^ tables::TABLES.edc[(edc & 0xFF) as usize];
}
self.data.write(edc);
}
}
impl std::fmt::Display for EDC {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "0x{:X}", self.data.read())
}
}
impl ECC {
pub fn calculate_for<T>(sector: &mut T) {
unsafe {
let mut raw_sector = std::mem::transmute::<&mut T, &mut [u8; SECTOR_SIZE]>(sector);
Self::calc_p_parity(&mut raw_sector);
Self::calc_q_parity(&mut raw_sector);
}
}
fn calc_parity(sector: &mut RawSector, offset: usize, len: usize, j0: usize, step1: usize, step2: usize) {
let mut src = 0xC;
let mut dst = 0x81C + offset;
let src_max = dst;
for _ in 0..len {
let base = src;
let mut x = 0u16;
let mut y = 0u16;
for j in j0..=42 {
x ^= tables::TABLES.gf8_product[j][sector[src + 0] as usize];
y ^= tables::TABLES.gf8_product[j][sector[src + 1] as usize];
src += step1;
if step1 == 2*44 && src >= src_max {
src = src - 2*1118;
}
}
sector[dst + 2*len + 0] = (x & 0x00FF) as u8; sector[dst + 0] = (x >> 8) as u8;
sector[dst + 2*len + 1] = (y & 0x00FF) as u8; sector[dst + 1] = (y >> 8) as u8;
dst += 2;
src = base + step2;
}
}
fn calc_p_parity(sector: &mut RawSector) {
Self::calc_parity(sector, 0, 43, 19, 2*43, 2);
}
fn calc_q_parity(sector: &mut RawSector) {
Self::calc_parity(sector, 43*4, 26, 0, 2*44, 2*43);
}
}
impl std::fmt::Display for ECC {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for n in (0..self.data.len()).step_by(4) {
let value = unsafe { *std::mem::transmute::<&u8, &u32>(&self.data[n]) };
if n != 0 && n%(4*4) == 0 {
write!(f, "\n")?;
}
write!(f, "0x{:08X} ", value)?;
}
std::fmt::Result::Ok(())
}
}
impl std::cmp::PartialEq for ECC {
fn eq(&self, other: &Self) -> bool {
for i in 0..std::mem::size_of::<ECC>() {
if self.data[i] != other.data[i] {
return false;
}
}
true
}
}
impl Default for ECC {
fn default() -> Self {
ECC{data: [0u8; 276]}
}
}

View File

@@ -0,0 +1,133 @@
pub static TABLES:ErrorCorrectionTables = ErrorCorrectionTables::new();
macro_rules! wrap_gr_eq {
($calc:expr, $tresh_hold:expr) => {
{
let value = $calc;
if value >= $tresh_hold {
value - $tresh_hold
}
else {
value
}
}
};
}
type EDC = [u32; 256];
type GF8LOG = [u8; 256];
type GF8ILOG = [u8; 256];
type GF8PRODUCT = [[u16; 256]; 43];
pub struct ErrorCorrectionTables {
pub edc: EDC,
pub gf8_log: GF8LOG,
pub gf8_ilog: GF8ILOG,
pub gf8_product: GF8PRODUCT
}
impl ErrorCorrectionTables {
const fn new() -> ErrorCorrectionTables {
let edc = Self::make_edc();
let (gf8_log, gf8_ilog) = Self::make_gf_logs();
let gf8_product = Self::make_gf_product(&gf8_log, &gf8_ilog);
ErrorCorrectionTables{edc, gf8_log, gf8_ilog, gf8_product}
}
const fn make_edc() -> EDC {
let mut edc = [0; 256];
let mut i = 0;
while i < 256 {
let mut x = i as u32;
let mut j = 0;
while j < 8 {
let carry = x & 0b1;
x >>= 1;
if carry != 0 {
x ^= 0xD8018001;
}
j += 1;
}
edc[i] = x;
i += 1;
}
edc
}
const fn make_gf_logs() -> (GF8LOG, GF8ILOG) {
let mut gf8_log = [0; 256];
let mut gf8_ilog = [0; 256];
gf8_log[0] = 0;
gf8_ilog[255] = 0;
let mut x = 1u8;
let mut i = 0u8;
while i < 255 {
gf8_log[x as usize] = i;
gf8_ilog[i as usize] = x;
let carry_8bit = x & 0b1000_0000;
x <<= 1;
if carry_8bit != 0 {
x ^= 0x1D;
}
i += 1;
}
(gf8_log, gf8_ilog)
}
const fn make_gf_product(gf8_log: &GF8LOG, gf8_ilog: &GF8ILOG) -> GF8PRODUCT {
let mut gf8_product: GF8PRODUCT = [[0; 256]; 43];
let mut j = 0;
while j < 43 {
let mut xx = gf8_ilog[44 - j];
let mut yy = Self::subfunc(xx^1, 0x19, &gf8_log, &gf8_ilog);
xx = Self::subfunc(xx, 0x1, &gf8_log, &gf8_ilog);
xx = Self::subfunc(xx^1, 0x18, &gf8_log, &gf8_ilog);
xx = gf8_log[xx as usize];
yy = gf8_log[yy as usize];
gf8_product[j][0] = 0;
let mut i = 1;
while i < 256 {
let x = wrap_gr_eq!(xx as u16 + gf8_log[i] as u16, 0xFF) as usize;
let y = wrap_gr_eq!(yy as u16 + gf8_log[i] as u16, 0xFF) as usize;
gf8_product[j][i] = gf8_ilog[x] as u16 + ((gf8_ilog[y] as u16) << 8);
i += 1;
}
j += 1;
}
gf8_product
}
const fn subfunc(mut a: u8, b: u8, gf8_log: &GF8LOG, gf8_ilog: &GF8ILOG) -> u8 {
if a > 0 {
let mut tmp_a:i32 = gf8_log[a as usize] as i32 - b as i32;
if tmp_a < 0 {
tmp_a += 0xFF;
}
a = gf8_ilog[tmp_a as usize];
}
a
}
}

View File

@@ -0,0 +1,56 @@
use super::sector;
#[macro_export]
macro_rules! read_write_bit_getter_setter {
($value:expr, $bit:expr, $name:expr) => {
paste::item! {
pub fn [< is_ $name >](&self) -> bool {
(self.$value.read() & (1 << $bit)) != 0
}
pub fn [< set_ $name >](&mut self) {
self.$value.write(self.$value.read() | (1 << $bit));
}
pub fn [< clear_ $name >](&mut self) {
self.$value.write(self.$value.read() & !(1 << $bit));
}
}
};
}
pub fn convert_ascii_to_str(bytes: &[u8]) -> Option<&str> {
if let Ok(str) = std::str::from_utf8(bytes) {
Some(str)
}
else {
None
}
}
pub fn force_convert_ascii_to_str(bytes: &[u8]) -> &str {
if let Some(str) = convert_ascii_to_str(bytes) {
str
}
else {
"<invalid UTF8 string>"
}
}
pub const fn sector_count_mode2_form1(data_size: usize) -> usize {
multiple_of_round_up(data_size, sector::Mode2Form1::DATA_SIZE)
}
pub const fn sector_count_mode2_form2(data_size: usize) -> usize {
multiple_of_round_up(data_size, sector::Mode2Form2::DATA_SIZE)
}
pub const fn round_bytes_mode2_form1(data_size: usize) -> usize {
multiple_of_round_up(data_size, sector::Mode2Form1::DATA_SIZE)*sector::Mode2Form1::DATA_SIZE
}
const fn multiple_of_round_up(size: usize, unit: usize) -> usize {
(size + (unit - 1))/unit
}

View File

@@ -0,0 +1,115 @@
use byteorder::{ByteOrder, BigEndian, LittleEndian};
pub trait ReadWriteEndian {
type UnderlayingType: std::fmt::Display;
fn read(&self) -> Self::UnderlayingType;
fn write(&mut self, value: Self::UnderlayingType);
}
pub trait EndianFamilyTypes {
type U16: ReadWriteEndian + std::default::Default;
type U32: ReadWriteEndian + std::default::Default;
}
pub struct BigEndianFamily {}
pub struct LittleEndianFamily {}
impl EndianFamilyTypes for BigEndianFamily {
type U16 = BigEndianU16;
type U32 = BigEndianU32;
}
impl EndianFamilyTypes for LittleEndianFamily {
type U16 = LittleEndianU16;
type U32 = LittleEndianU32;
}
macro_rules! make_lsb_msb {
($type_val:ty) => {
paste::item! {
#[derive(Debug, Default, Copy, Clone)]
#[repr(packed(1))]
pub struct [< LittleEndian $type_val:upper >] {
value: [u8; std::mem::size_of::<$type_val>()]
}
#[derive(Debug, Default, Copy, Clone)]
#[repr(packed(1))]
pub struct [< BigEndian $type_val:upper >] {
value: [u8; std::mem::size_of::<$type_val>()]
}
#[derive(Debug, Default, Copy, Clone)]
#[repr(packed(1))]
pub struct [< LittleBigEndian $type_val:upper >] {
pub lsb: [< LittleEndian $type_val:upper >],
pub msb: [< BigEndian $type_val:upper >],
}
impl [< LittleEndian $type_val:upper >] {
pub fn new(value: $type_val) -> [< LittleEndian $type_val:upper >] {
let mut new_value = [< LittleEndian $type_val:upper >]::default();
new_value.write(value);
new_value
}
}
impl ReadWriteEndian for [< LittleEndian $type_val:upper >] {
type UnderlayingType = $type_val;
fn read(&self) -> Self::UnderlayingType {
LittleEndian::[< read_ $type_val >](&self.value)
}
fn write(&mut self, value: Self::UnderlayingType) {
LittleEndian::[< write_ $type_val >](&mut self.value, value);
}
}
impl [< BigEndian $type_val:upper >] {
pub fn new(value: $type_val) -> [< BigEndian $type_val:upper >] {
let mut new_value = [< BigEndian $type_val:upper >]::default();
new_value.write(value);
new_value
}
}
impl ReadWriteEndian for [< BigEndian $type_val:upper >] {
type UnderlayingType = $type_val;
fn read(&self) -> Self::UnderlayingType {
BigEndian::[< read_ $type_val >](&self.value)
}
fn write(&mut self, value: Self::UnderlayingType) {
BigEndian::[< write_ $type_val >](&mut self.value, value);
}
}
impl [< LittleBigEndian $type_val:upper >] {
pub fn new(value: $type_val) -> [< LittleBigEndian $type_val:upper >] {
let mut new_value = [< LittleBigEndian $type_val:upper >]::default();
new_value.write(value);
new_value
}
pub fn read_any(&self) -> $type_val {
self.lsb.read()
}
pub fn write(&mut self, value: $type_val) {
self.lsb.write(value);
self.msb.write(value);
}
}
}
};
}
make_lsb_msb!(u16);
make_lsb_msb!(i16);
make_lsb_msb!(u32);

View File

@@ -0,0 +1,12 @@
pub mod bcd;
pub mod cue;
pub mod cdstring;
pub mod date;
pub mod dir_record;
pub mod error_correction;
pub mod helper;
pub mod lsb_msb;
pub mod path_table;
pub mod pvd;
pub mod time;
pub mod sector;

View File

@@ -0,0 +1,73 @@
use super::{helper::{force_convert_ascii_to_str}, lsb_msb::{EndianFamilyTypes, ReadWriteEndian, LittleEndianFamily, BigEndianFamily}};
pub type PathTableL = PathTableBase<LittleEndianFamily>;
pub type PathTableM = PathTableBase<BigEndianFamily>;
#[repr(packed(1))]
pub struct PathTableBase<EndianFamily: EndianFamilyTypes> {
pub name_length: [u8; 1],
pub extended_attribute_length: [u8; 1],
pub directory_logical_block: EndianFamily::U32,
pub parent_logical_block: EndianFamily::U16,
//name: DString<name_length>
//padding: only if name_length is odd
}
impl<EndianFamily: EndianFamilyTypes> PathTableBase<EndianFamily> {
pub unsafe fn new(&mut self, name: &str) {
*self = PathTableBase{
name_length: [name.len() as u8],
extended_attribute_length: [0],
directory_logical_block: EndianFamily::U32::default(),
parent_logical_block: EndianFamily::U16::default(),
};
self.set_name(name);
}
pub fn get_name(&self) -> &str {
unsafe {
let raw_str:*const u8 = (&self.name_length as *const u8).add(std::mem::size_of::<Self>());
force_convert_ascii_to_str(std::slice::from_raw_parts(raw_str, self.name_length[0] as usize))
}
}
pub unsafe fn set_name(&mut self, name: &str) {
let mut raw_str:*mut u8 = (&mut self.name_length as *mut u8).add(std::mem::size_of::<Self>());
for char in name.bytes() {
*raw_str = char;
raw_str = raw_str.add(1);
}
}
pub fn get_size(&self) -> usize {
let name_length = self.name_length[0] as usize;
std::mem::size_of::<PathTableBase<EndianFamily>>() + name_length + Self::padding_value(name_length)
}
pub fn calculate_size_for(name: &str) -> usize {
let name_length = name.len();
std::mem::size_of::<PathTableBase<EndianFamily>>() + name_length + Self::padding_value(name_length)
}
fn padding_value(name_length: usize) -> usize {
if (name_length & 0x1) != 0 {
1usize
}
else {
0usize
}
}
}
impl<EndianFamily: EndianFamilyTypes> std::fmt::Display for PathTableBase<EndianFamily> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (dir_block, parent_block) = unsafe {
(std::ptr::addr_of!(self.directory_logical_block).read_unaligned().read(), std::ptr::addr_of!(self.parent_logical_block).read_unaligned().read())
};
write!(f, "\"{}\" @{} ^{}", self.get_name(), dir_block, parent_block)
}
}

View File

@@ -0,0 +1,143 @@
use super::{cdstring::{AString, DString}, date::Date, dir_record::DirectoryRecord, lsb_msb::{ReadWriteEndian, BigEndianU32, LittleEndianU32, LittleBigEndianU32, LittleBigEndianU16}, sector::Mode2Form1};
use super::helper::force_convert_ascii_to_str as obtain_string;
const _: () = assert!(std::mem::size_of::<PrimaryVolumeDescriptor>() == Mode2Form1::DATA_SIZE, "PVD wrong data size");
#[repr(packed(1))]
pub struct PrimaryVolumeDescriptor {
pub volume_type: [u8; 1], // 1 for Primary Volume Descriptor
pub std_id: [u8; 5], // "CD001"
pub version: [u8; 1], // 1 for Standard
pub reserved_1: [u8; 1],
pub system_id: AString::<32>, // "PLAYSTATION"
pub volume_id: DString::<32>, // ???
pub reserved_2: [u8; 8],
pub vol_space_size: LittleBigEndianU32, // number of logical blocks
pub reserved_3: [u8;32],
pub vol_set_size: LittleBigEndianU16, // Usually 1
pub vol_seq_number: LittleBigEndianU16, // Usually 1
pub block_size: LittleBigEndianU16, // Size in bytes of a logical block
pub path_table_size: LittleBigEndianU32, // Maximum 0x800 for PSX
pub path_table_1: LittleEndianU32, // Start of Table1
pub path_table_2: LittleEndianU32, // Start of Table2
pub path_table_3: BigEndianU32, // Start of Table3
pub path_table_4: BigEndianU32, // Start of Table4
pub root_dir_record: [u8; 34], // Root directory record
pub volume_set_id: DString::<128>, // String which is usually empty
pub publisher_id: AString::<128>, // Company name of publisher
pub data_preparer: AString::<128>, //
pub app_id: AString::<128>, // "PLAYSTATION"
pub copyright_file: [u8; 37], // FILENAME.EXT;VER
pub abstract_file: [u8; 37], // Empty
pub bibliograph_file: [u8; 37], // Empty
pub vol_create_time: Date, // YYYYMMDDHHMMSSFF
pub vol_modification_time: Date, // YYYYMMDDHHMMSSFF all zero
pub vol_expiration_time: Date, // YYYYMMDDHHMMSSFF all zero
pub vol_effective_time: Date, // YYYYMMDDHHMMSSFF all zero
pub file_struct_version: [u8; 1], // 1 Standard
pub reserved_4: [u8; 1],
pub app_use_area: [u8; 141], // zero for PSX and VCD
pub cd_xa_id: [u8;8], // "CD-XA001" for PSX and VCD
pub cd_xa_flags: [u8;2], // zero for PSX and VCD
pub cd_xa_startup_dir: [u8; 8], // zero for PSX and VCD
pub cd_xa_reserved: [u8; 8], // zero for PSX and VCD
pub app_use_area_2: [u8; 345], // zero for PSX and VCD
pub reserved_5: [u8; 653]
}
#[repr(packed(1))]
pub struct VolumeDescriptorTerminator {
pub volume_type: [u8; 1], //0xFF for Terminator
pub std_id: [u8; 5], //"CD0001"
pub version: [u8; 1], //0x01 for Standard
pub reserved: [u8; 2041] //Zero filled
}
impl PrimaryVolumeDescriptor {
pub fn new() -> PrimaryVolumeDescriptor {
PrimaryVolumeDescriptor{
volume_type: [1],
std_id: ['C' as u8, 'D' as u8, '0' as u8, '0' as u8, '1' as u8],
version: [1],
reserved_1: [0],
system_id: AString::default(),
volume_id: DString::default(),
reserved_2: [0; 8],
vol_space_size: LittleBigEndianU32::default(),
reserved_3: [0; 32],
vol_set_size: LittleBigEndianU16::new(0x0001),
vol_seq_number: LittleBigEndianU16::new(0x0001),
block_size: LittleBigEndianU16::new(0x0800),
path_table_size: LittleBigEndianU32::default(),
path_table_1: LittleEndianU32::default(),
path_table_2: LittleEndianU32::default(),
path_table_3: BigEndianU32::default(),
path_table_4: BigEndianU32::default(),
root_dir_record: [0; 34],
volume_set_id: DString::default(),
publisher_id: AString::default(),
data_preparer: AString::default(),
app_id: AString::default(),
copyright_file: [' ' as u8; 37],
abstract_file: [' ' as u8; 37],
bibliograph_file: [' ' as u8; 37],
vol_create_time: Date::new(),
vol_modification_time: Date::new(),
vol_expiration_time: Date::new(),
vol_effective_time: Date::new(),
file_struct_version: [1],
reserved_4: [0],
app_use_area: [0; 141],
cd_xa_id: ['C' as u8, 'D' as u8, '-' as u8, 'X' as u8, 'A' as u8, '0' as u8, '0' as u8, '1' as u8],
cd_xa_flags: [0; 2],
cd_xa_startup_dir: [0; 8],
cd_xa_reserved: [0; 8],
app_use_area_2: [0; 345],
reserved_5: [0; 653]
}
}
pub fn get_root_dir_record(&self) -> &DirectoryRecord {
unsafe {
std::mem::transmute::<&[u8; 34], &DirectoryRecord>(&self.root_dir_record)
}
}
pub fn get_root_dir_record_mut(&mut self) -> &mut DirectoryRecord {
unsafe {
std::mem::transmute::<&mut [u8; 34], &mut DirectoryRecord>(&mut self.root_dir_record)
}
}
}
impl std::fmt::Display for PrimaryVolumeDescriptor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let root_dir = self.get_root_dir_record();
write!(f, "Volume Descriptor Type: {}\nStandard Identifier: {}\n", self.volume_type[0], obtain_string(&self.std_id))?;
write!(f, "Volume Descriptor Version: {}\nSystem Identifier: {}\n", self.version[0], obtain_string(self.system_id.as_raw()))?;
write!(f, "Volume Identifier: {}\nVolume Space Size: {}\n", obtain_string(self.volume_id.as_raw()), self.vol_space_size.read_any())?;
write!(f, "Voulme Set Size: {}\nVolume Sequence Number: {}\n", self.vol_set_size.read_any(), self.vol_seq_number.read_any())?;
write!(f, "Logical Block Size (Bytes): {}\nPath Table Size (Bytes): {}\n", self.block_size.read_any(), self.path_table_size.read_any())?;
write!(f, "Path Table 1: {}\nPath Table 2: {}\n", self.path_table_1.read(), self.path_table_2.read())?;
write!(f, "Path Table 3: {}\nPath Table 4: {}\n", self.path_table_3.read(), self.path_table_4.read())?;
write!(f, "Root Directory Record: \n{}\nVolume Set Identifier: {}\n", root_dir, obtain_string(self.volume_set_id.as_raw()))?;
write!(f, "Publisher Identifier: {}\nData Preparer Identifier: {}\n", obtain_string(self.publisher_id.as_raw()), obtain_string(self.data_preparer.as_raw()))?;
write!(f, "Application Identifier: {}\nCopyright Filename: {}\n", obtain_string(self.app_id.as_raw()), obtain_string(&self.copyright_file))?;
write!(f, "Abstract Filename: {}\nBibliographic Filename: {}\n", obtain_string(&self.abstract_file), obtain_string(&self.bibliograph_file))?;
write!(f, "Volume Creation Time: {}\nVolume Modification Time: {}\n", self.vol_create_time, self.vol_modification_time)?;
write!(f, "Volume Expiration Time: {}\nVolume Effective Time: {}\n", self.vol_expiration_time, self.vol_effective_time)?;
write!(f, "File Sturcture Version: {}\nCD-XA Identifier: {}\n", self.file_struct_version[0], obtain_string(&self.cd_xa_id))
}
}
impl VolumeDescriptorTerminator {
pub fn new() -> VolumeDescriptorTerminator {
VolumeDescriptorTerminator {
volume_type: [0xFF],
std_id: ['C' as u8, 'D' as u8, '0' as u8, '0' as u8, '1' as u8],
version: [0x01],
reserved: [0; 2041]
}
}
}

View File

@@ -0,0 +1,512 @@
use super::{bcd::BCDValue, error_correction::{ECC, EDC}, lsb_msb::LittleEndianI16, time::Time};
pub const SECTOR_SIZE:usize = 2352;
const _: () = assert!(std::mem::size_of::<Mode0>() == SECTOR_SIZE, "Mode0 wrong Sector size");
const _: () = assert!(std::mem::size_of::<Mode1>() == SECTOR_SIZE, "Mode1 wrong Sector size");
const _: () = assert!(std::mem::size_of::<Mode2Form1>() == SECTOR_SIZE, "Mode2From1 wrong Sector size");
const _: () = assert!(std::mem::size_of::<Mode2Form2>() == SECTOR_SIZE, "Mode2From2 wrong Sector size");
macro_rules! bit_getter_setter {
($value:expr, $bit:expr, $name:expr) => {
paste::item! {
pub fn [< set_ $name >](&mut self) {
self.$value |= (1 << $bit);
}
pub fn [< clear_ $name >](&mut self) {
self.$value &= !(1 << $bit);
}
pub fn [< is_ $name >](&self) -> bool {
(self.$value & (1 << $bit)) != 0
}
}
};
}
pub enum HeaderMode {
Mode0 = 0,
Mode1 = 1,
Mode2 = 2,
}
pub enum SubHeaderForm {
Form1,
Form2
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct Sync {
value: [u8; 12],
}
impl Sync {
pub fn new() -> Sync {
Sync{value: [0x0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0]}
}
}
impl std::fmt::Display for Sync {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "< ")?;
for byte in self.value {
write!(f, "{} ", byte)?;
}
write!(f, ">")
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct Header {
minute: BCDValue,
second: BCDValue,
sector: BCDValue,
mode: u8
}
impl Header {
pub fn new(mode: u8) -> Header {
Header{minute: BCDValue::from(0), second: BCDValue::from(0), sector: BCDValue::from(0), mode}
}
pub fn set_time(&mut self, time: Time) {
let (min, sec, sector) = time.to_bcd();
self.minute = min;
self.second = sec;
self.sector = sector;
}
pub fn get_mode(&self) -> Result<HeaderMode, u8> {
match self.mode {
0 => Ok(HeaderMode::Mode0),
1 => Ok(HeaderMode::Mode1),
2 => Ok(HeaderMode::Mode2),
_ => Err(self.mode),
}
}
}
impl std::fmt::Display for Header {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "min: {}, sec: {}, sector: {}, mode: {}", u8::from(&self.minute), u8::from(&self.second), u8::from(&self.sector), self.mode)
}
}
impl Default for Header {
fn default() -> Self {
Header{minute: BCDValue::from(0), second: BCDValue::from(0), sector: BCDValue::from(0), mode: 0}
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct SubHeader {
pub file_number: u8,
pub channel_number: u8,
pub sub_mode: SubMode,
pub coding_info: CodingInfo,
}
impl SubHeader {
pub fn default_form1() -> SubHeader {
SubHeader{file_number: 0, channel_number: 0, sub_mode: SubMode::default_form1(), coding_info: CodingInfo::default_form1()}
}
pub fn default_form2() -> SubHeader {
SubHeader{file_number: 0, channel_number: 0, sub_mode: SubMode::default_form2(), coding_info: CodingInfo::default_form2()}
}
pub fn get_form(&self) -> SubHeaderForm {
if self.sub_mode.is_form2() {
SubHeaderForm::Form2
}
else {
SubHeaderForm::Form1
}
}
}
impl std::fmt::Display for SubHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "file: {}, channel: {}\nSubmode:\n{}\nCodinginfo:\n\t{}", self.file_number, self.channel_number, self.sub_mode, {
if self.sub_mode.is_data() {
&"Reserved (0x00)" as &dyn std::fmt::Display
}
else {
&self.coding_info as &dyn std::fmt::Display
}
})
}
}
impl Default for SubHeader {
fn default() -> Self {
SubHeader{file_number: 0, channel_number: 0, sub_mode: SubMode::default(), coding_info: CodingInfo::default()}
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct SubMode {
value: u8
}
impl SubMode {
pub fn default_form1() -> SubMode {
let mut sub_mode = SubMode{value: 0};
sub_mode.set_data();
sub_mode.clear_form2();
sub_mode
}
pub fn default_form2() -> SubMode {
let mut sub_mode = SubMode{value: 0};
sub_mode.set_audio();
sub_mode.set_form2();
sub_mode
}
bit_getter_setter!(value, 0, eor);
bit_getter_setter!(value, 1, video);
bit_getter_setter!(value, 2, audio);
bit_getter_setter!(value, 3, data);
bit_getter_setter!(value, 4, trigger);
bit_getter_setter!(value, 5, form2);
bit_getter_setter!(value, 6, real_time);
bit_getter_setter!(value, 7, eof);
}
impl std::fmt::Display for SubMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\tEOR: {}, Video: {}\n\tAudio: {}, Data: {}\n\tTrigger: {}, Form2: {}\n\tRealTime: {}, EOF: {}",
self.is_eor(), self.is_video(), self.is_audio(), self.is_data(), self.is_trigger(), self.is_form2(), self.is_real_time(), self.is_eof())
}
}
impl Default for SubMode {
fn default() -> Self {
SubMode{value: 0}
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct CodingInfo {
value: u8
}
impl CodingInfo {
pub fn default_form1() -> CodingInfo {
CodingInfo{value: 0}
}
pub fn default_form2() -> CodingInfo {
CodingInfo{value: 1}
}
pub fn get_sound_type(&self) -> XAADPCMSound {
match self.value & (0b11) {
0 => XAADPCMSound::Mono,
1 => XAADPCMSound::Stereo,
_ => XAADPCMSound::Reserved
}
}
pub fn set_sound_type(&mut self, sound_type: XAADPCMSound) {
self.value &= !0b11;
self.value |= sound_type as u8;
}
pub fn get_sample_rate(&self) -> XAADPCMSampleRate {
match (self.value >> 2) & 0b11 {
0 => XAADPCMSampleRate::Freq37800Hz,
1 => XAADPCMSampleRate::Freq18900Hz,
_ => XAADPCMSampleRate::Reserved
}
}
pub fn set_sample_rate(&mut self, sample_rate: XAADPCMSampleRate) {
self.value &= !(0b11 << 2);
self.value |= (sample_rate as u8) << 2;
}
pub fn get_bits_per_sample(&self) -> XAADPCMBitsPerSample {
match (self.value >> 4) & 0b11 {
0 => XAADPCMBitsPerSample::Normal,
1 => XAADPCMBitsPerSample::High,
_ => XAADPCMBitsPerSample::Reserved
}
}
pub fn set_bits_per_sample(&mut self, bits: XAADPCMBitsPerSample) {
self.value &= !(0b11 << 4);
self.value |= (bits as u8) << 4;
}
pub fn has_emphasis(&self) -> bool {
self.value & (1 << 6) != 0
}
pub fn set_emphasis(&mut self, has_emphasis: bool) {
let emphasis = {
if has_emphasis {
1 << 6
}
else {
0
}
};
self.value &= !(1 << 6);
self.value |= emphasis;
}
}
impl std::fmt::Display for CodingInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Sound: {} Sample Rate: {} Bits per Sample: {}, Emphasis: {}", self.get_sound_type().as_str(), self.get_sample_rate().as_str(), self.get_bits_per_sample().as_str(), self.has_emphasis())
}
}
impl Default for CodingInfo {
fn default() -> Self {
CodingInfo{value: 0}
}
}
#[repr(packed(1))]
#[derive(Copy, Clone, Debug)]
pub struct AudioSample {
pub left_sample: LittleEndianI16,
pub right_sample: LittleEndianI16
}
impl AudioSample {
pub fn new(left: i16, right: i16) -> AudioSample {
AudioSample{left_sample: LittleEndianI16::new(left), right_sample: LittleEndianI16::new(right)}
}
}
impl std::default::Default for AudioSample {
fn default() -> AudioSample {
AudioSample::new(0, 0)
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct Audio {
samples: [AudioSample; Self::SAMPLE_SIZE]
}
impl Audio {
pub const DATA_SIZE:usize = 2352;
pub const SAMPLE_SIZE:usize = Self::DATA_SIZE/std::mem::size_of::<AudioSample>();
pub fn new() -> Audio {
Audio{samples: [AudioSample::default(); Self::SAMPLE_SIZE]}
}
pub fn get_raw_samples(&self) -> &[u8] {
unsafe {
std::mem::transmute::<&[AudioSample; 588], &[u8; Self::DATA_SIZE]>(&self.samples)
}
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode0 {
pub sync: Sync,
pub header: Header,
pub zero: [u8;2336]
}
impl Mode0 {
pub fn new() -> Mode0 {
Mode0{sync: Sync::new(), header: Header::new(0), zero: [0; 2336]}
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode1 {
pub sync: Sync,
pub header: Header,
pub data: [u8; Mode1::DATA_SIZE],
pub edc: EDC,
pub zero: [u8; 8],
pub ecc: ECC
}
impl Mode1 {
pub const DATA_SIZE:usize = 2048;
pub fn new() -> Mode1 {
Mode1{sync: Sync::new(), header: Header::new(1), data: [0; Self::DATA_SIZE], edc: EDC::default(), zero: [0u8; 8], ecc: ECC::default()}
}
pub fn finalize(&mut self) {
self.calculate_edc();
self.calculate_ecc();
}
pub fn calculate_edc(&mut self) {
unsafe {
let raw = std::mem::transmute::<&Mode1, &[u8;std::mem::size_of::<Mode1>()]>(self);
self.edc = EDC::calculate_for(&raw[0..=0x80F]);
}
}
pub fn calculate_ecc(&mut self) {
ECC::calculate_for(self);
}
}
pub type Mode2 = Mode2Form1;
#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode2Form1 {
pub sync: Sync,
pub header: Header,
pub sub_header: SubHeader,
pub copy_sub_header: SubHeader,
pub data: [u8; Mode2Form1::DATA_SIZE],
pub edc: EDC,
pub ecc: ECC
}
impl Mode2Form1 {
pub const DATA_SIZE:usize = 2048;
pub fn new() -> Mode2Form1 {
let sub_header = SubHeader::default_form1();
let copy_sub_header = sub_header.clone();
Mode2Form1{sync: Sync::new(), header: Header::new(2), sub_header, copy_sub_header, data: [0; Self::DATA_SIZE], edc: EDC::default(), ecc: ECC::default()}
}
pub fn set_end_of_file(&mut self) {
self.sub_header.sub_mode.set_eor();
self.sub_header.sub_mode.set_eof();
}
pub fn finalize(&mut self) {
self.copy_sub_header = self.sub_header.clone();
self.calculate_edc();
self.calculate_ecc();
}
pub fn calculate_edc(&mut self) {
unsafe {
let raw = std::mem::transmute::<&Mode2Form1, &[u8;std::mem::size_of::<Mode2Form1>()]>(self);
self.edc = EDC::calculate_for(&raw[0x10..=0x817]);
}
}
pub fn calculate_ecc(&mut self) {
let header = std::mem::take(&mut self.header);
ECC::calculate_for(self);
self.header = header;
}
}
#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode2Form2 {
pub sync: Sync,
pub header: Header,
pub sub_header: SubHeader,
pub copy_sub_header: SubHeader,
pub data: [u8; Mode2Form2::DATA_SIZE],
pub edc: EDC,
}
impl Mode2Form2 {
pub const DATA_SIZE:usize = 2324;
pub fn new() -> Mode2Form2 {
let sub_header = SubHeader::default_form2();
let copy_sub_header = sub_header.clone();
Mode2Form2{sync: Sync::new(), header: Header::new(2), sub_header, copy_sub_header, data: [0; Self::DATA_SIZE], edc: EDC::default()}
}
pub fn set_end_of_file(&mut self) {
self.sub_header.sub_mode.set_eor();
self.sub_header.sub_mode.set_eof();
}
pub fn finalize(&mut self) {
self.copy_sub_header = self.sub_header.clone();
self.calculate_edc();
}
pub fn calculate_edc(&mut self) {
unsafe {
let raw = std::mem::transmute::<&Mode2Form2, &[u8;std::mem::size_of::<Mode2Form2>()]>(self);
self.edc = EDC::calculate_for(&raw[0x10..=0x92B]);
}
}
}
pub enum XAADPCMSound {
Mono = 0,
Stereo = 1,
Reserved = 2,
}
impl XAADPCMSound {
pub fn as_str(&self) -> &str {
match self {
Self::Mono => "Mono",
Self::Stereo => "Stereo",
Self::Reserved => "Reserved"
}
}
}
pub enum XAADPCMBitsPerSample {
Normal = 0,
High = 1,
Reserved = 2
}
impl XAADPCMBitsPerSample {
pub fn as_str(&self) -> &str {
match self {
Self::Normal => "Normal (4bit)",
Self::High => "High (8bit)",
Self::Reserved => "Reserved"
}
}
}
pub enum XAADPCMSampleRate {
Freq37800Hz = 0,
Freq18900Hz = 1,
Reserved = 2,
}
impl XAADPCMSampleRate {
pub fn as_str(&self) -> &str {
match self {
Self::Freq37800Hz => "37800Hz",
Self::Freq18900Hz => "18900Hz",
Self::Reserved => "Reserved",
}
}
}

View File

@@ -0,0 +1,90 @@
use super::bcd::BCDValue;
use super::super::Error;
#[derive(Clone)]
pub struct Time {
minute: u8,
second: u8,
sector: u8
}
impl Time {
pub const MAX_SECTORS:usize = 75;
pub const MAX_SECONDS:usize = 60;
pub const MAX_MINUTES:usize = 74;
pub fn cue_start() -> Time {
Time{minute: 0, second: 0, sector: 0}
}
pub fn cd_start() -> Time {
Time{minute: 0, second: 2, sector: 0}
}
pub fn cd_pregap() -> Time {
Time{minute: 0, second: 2, sector: 0}
}
pub fn add_sectors(&mut self, sector: usize) -> Result<(), Error> {
let sector = self.sector as usize + sector;
let second = self.second as usize + (sector/Self::MAX_SECTORS);
let minute = self.minute as usize + (second/Self::MAX_SECONDS);
if minute > Self::MAX_MINUTES {
Err(Error::TimeToLarge)
}
else {
*self = Time{minute: minute as u8, second: (second%Self::MAX_SECONDS) as u8, sector: (sector%Self::MAX_SECTORS) as u8};
Ok(())
}
}
pub fn parse(string: &str) -> Option<Time> {
let mut slices = string.split(':');
let minutes = slices.next()?;
let seconds = slices.next()?;
let sectors = slices.next()?;
let mut time = Time{minute: minutes.parse().ok()?, second: seconds.parse().ok()?, sector: sectors.parse().ok()?};
time.add_sectors(0).ok()?;
Some(time)
}
pub fn add_sector(&mut self) -> Result<(), Error> {
self.add_sectors(1)
}
pub fn to_bcd(&self) -> (BCDValue, BCDValue, BCDValue) {
(BCDValue::from(self.minute), BCDValue::from(self.second), BCDValue::from(self.sector))
}
pub fn to_lba(&self) -> usize {
self.sector as usize + (self.second as usize*Self::MAX_SECTORS) + (self.minute as usize*Self::MAX_SECONDS*Self::MAX_SECTORS)
}
pub fn from_lba(lba: usize) -> Time {
let sector = (lba%Time::MAX_SECTORS) as u8;
let second = ((lba/Time::MAX_SECTORS)%Time::MAX_SECONDS) as u8;
let minute = (((lba/Time::MAX_SECTORS)/Time::MAX_SECONDS)%Time::MAX_MINUTES) as u8;
Time{sector, second, minute}
}
pub fn dump(&self) -> String {
format!("min: {}, sec: {}, sector: {}", self.minute, self.second, self.sector)
}
}
impl std::convert::From<(BCDValue, BCDValue, BCDValue)> for Time {
fn from(time: (BCDValue, BCDValue, BCDValue)) -> Self {
Time{minute: u8::from(time.0), second: u8::from(time.1), sector: u8::from(time.2)}
}
}
impl std::fmt::Display for Time {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:0>2}:{:0>2}:{:0>2}", self.minute, self.second, self.sector)
}
}