use crate::scarletbook::consts;
use anyhow::Result;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{self, Read};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum FrameFormat {
Dst = 0,
Reserved = 1,
Dsd3In14 = 2,
Dsd3In16 = 3,
Dsd4 = 4,
Dsd5 = 5,
Dsd6 = 6,
Dsd7 = 7,
Unknown(u8),
}
impl From<u8> for FrameFormat {
fn from(val: u8) -> Self {
match val {
0 => FrameFormat::Dst,
1 => FrameFormat::Reserved,
2 => FrameFormat::Dsd3In14,
3 => FrameFormat::Dsd3In16,
4 => FrameFormat::Dsd4,
5 => FrameFormat::Dsd5,
6 => FrameFormat::Dsd6,
7 => FrameFormat::Dsd7,
n => FrameFormat::Unknown(n),
}
}
}
impl FrameFormat {
pub fn sectors_per_frame(&self) -> Option<u32> {
match self {
FrameFormat::Dst => Some(14), FrameFormat::Dsd3In14 => Some(14),
FrameFormat::Dsd3In16 => Some(16),
FrameFormat::Dsd4 | FrameFormat::Dsd5 | FrameFormat::Dsd6 | FrameFormat::Dsd7 => {
Some(14)
}
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PlayTime {
pub minutes: u8, pub seconds: u8, pub frames: u8, }
impl PlayTime {
pub fn parse<R: Read>(reader: &mut R) -> io::Result<Self> {
Ok(PlayTime {
minutes: reader.read_u8()?,
seconds: reader.read_u8()?,
frames: reader.read_u8()?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GenreTable {
pub category: u8, pub reserved: u8,
pub genre: u16, }
impl GenreTable {
pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
Ok(Self {
category: reader.read_u8()?,
reserved: reader.read_u8()?,
genre: reader.read_u16::<BigEndian>()?,
})
}
pub fn category_name(&self) -> Option<&'static str> {
match self.category {
0 => Some("Not used"),
1 => Some("General"),
2 => Some("Japanese"),
_ => None,
}
}
pub fn genre_name(&self) -> Option<&'static str> {
if self.category == 1 && (self.genre as usize) < consts::GENRE_NAMES.len() {
Some(consts::GENRE_NAMES[self.genre as usize])
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LocaleTable {
pub language_code: [u8; 2],
pub character_set: u8,
pub _reserved: u8,
}
impl LocaleTable {
pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
let mut language_code = [0u8; 2];
reader.read_exact(&mut language_code)?;
Ok(Self {
language_code,
character_set: reader.read_u8()?,
_reserved: reader.read_u8()?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Version {
pub major: u8,
pub minor: u8,
}
impl Version {
pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
Ok(Self {
major: reader.read_u8()?,
minor: reader.read_u8()?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TrackTextType {
Title = 0x01,
Performer = 0x02,
Songwriter = 0x03,
Composer = 0x04,
Arranger = 0x05,
Message = 0x06,
ExtraMessage = 0x07,
Copyright = 0x08,
TitlePhonetic = 0x81,
PerformerPhonetic = 0x82,
SongwriterPhonetic = 0x83,
ComposerPhonetic = 0x84,
ArrangerPhonetic = 0x85,
MessagePhonetic = 0x86,
ExtraMessagePhonetic = 0x87,
CopyrightPhonetic = 0x88,
Unknown(u8),
}
impl From<u8> for TrackTextType {
fn from(val: u8) -> Self {
match val {
0x01 => TrackTextType::Title,
0x02 => TrackTextType::Performer,
0x03 => TrackTextType::Songwriter,
0x04 => TrackTextType::Composer,
0x05 => TrackTextType::Arranger,
0x06 => TrackTextType::Message,
0x07 => TrackTextType::ExtraMessage,
0x08 => TrackTextType::Copyright,
0x81 => TrackTextType::TitlePhonetic,
0x82 => TrackTextType::PerformerPhonetic,
0x83 => TrackTextType::SongwriterPhonetic,
0x84 => TrackTextType::ComposerPhonetic,
0x85 => TrackTextType::ArrangerPhonetic,
0x86 => TrackTextType::MessagePhonetic,
0x87 => TrackTextType::ExtraMessagePhonetic,
0x88 => TrackTextType::CopyrightPhonetic,
n => TrackTextType::Unknown(n),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TrackTime {
pub minutes: u8, pub seconds: u8, pub frames: u8, pub flags: u8, }
impl TrackTime {
pub fn parse<R: Read>(reader: &mut R) -> io::Result<Self> {
Ok(TrackTime {
minutes: reader.read_u8()?,
seconds: reader.read_u8()?,
frames: reader.read_u8()?,
flags: reader.read_u8()?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Isrc {
pub country_code: [u8; 2], pub owner_code: [u8; 3], pub recording_year: [u8; 2], pub designation_code: [u8; 5], }
impl Isrc {
pub fn parse<R: Read>(reader: &mut R) -> io::Result<Self> {
let mut country_code = [0u8; 2];
reader.read_exact(&mut country_code)?;
let mut owner_code = [0u8; 3];
reader.read_exact(&mut owner_code)?;
let mut recording_year = [0u8; 2];
reader.read_exact(&mut recording_year)?;
let mut designation_code = [0u8; 5];
reader.read_exact(&mut designation_code)?;
Ok(Isrc {
country_code,
owner_code,
recording_year,
designation_code,
})
}
pub fn is_valid(&self) -> bool {
self.country_code[0] != 0
}
}
impl std::fmt::Display for Isrc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&String::from_utf8_lossy(&self.country_code))?;
f.write_str(&String::from_utf8_lossy(&self.owner_code))?;
f.write_str(&String::from_utf8_lossy(&self.recording_year))?;
f.write_str(&String::from_utf8_lossy(&self.designation_code))
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TrackText {
pub title: Option<String>,
pub performer: Option<String>,
pub songwriter: Option<String>,
pub composer: Option<String>,
pub arranger: Option<String>,
pub message: Option<String>,
pub extra_message: Option<String>,
pub copyright: Option<String>,
pub title_phonetic: Option<String>,
pub performer_phonetic: Option<String>,
pub songwriter_phonetic: Option<String>,
pub composer_phonetic: Option<String>,
pub arranger_phonetic: Option<String>,
pub message_phonetic: Option<String>,
pub extra_message_phonetic: Option<String>,
pub copyright_phonetic: Option<String>,
}
impl TrackText {
pub fn set_text(&mut self, text_type: TrackTextType, text: String) {
if text.is_empty() {
return;
}
let target = match text_type {
TrackTextType::Title => &mut self.title,
TrackTextType::Performer => &mut self.performer,
TrackTextType::Songwriter => &mut self.songwriter,
TrackTextType::Composer => &mut self.composer,
TrackTextType::Arranger => &mut self.arranger,
TrackTextType::Message => &mut self.message,
TrackTextType::ExtraMessage => &mut self.extra_message,
TrackTextType::Copyright => &mut self.copyright,
TrackTextType::TitlePhonetic => &mut self.title_phonetic,
TrackTextType::PerformerPhonetic => &mut self.performer_phonetic,
TrackTextType::SongwriterPhonetic => &mut self.songwriter_phonetic,
TrackTextType::ComposerPhonetic => &mut self.composer_phonetic,
TrackTextType::ArrangerPhonetic => &mut self.arranger_phonetic,
TrackTextType::MessagePhonetic => &mut self.message_phonetic,
TrackTextType::ExtraMessagePhonetic => &mut self.extra_message_phonetic,
TrackTextType::CopyrightPhonetic => &mut self.copyright_phonetic,
TrackTextType::Unknown(_) => return,
};
*target = Some(text);
}
}