From 291e0ba04998a5fedc1b605f991aee8a26d46e39 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Tue, 28 May 2024 21:44:15 -0500 Subject: [PATCH] Implemented all functions in `commands.rs` --- .cargo/config.toml | 4 + Cargo.toml | 5 +- src/netmd/base.rs | 119 ++-- src/netmd/commands.rs | 344 +++++++-- src/netmd/encryption.rs | 2 + src/netmd/interface.rs | 21 +- src/netmd/mappings.rs | 1461 +++++++++++++++++---------------------- src/netmd/utils.rs | 151 +++- 8 files changed, 1126 insertions(+), 981 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index db7e33d..dc382b6 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,6 @@ [target.'cfg(target_family = "wasm")'] rustflags = ["--cfg=web_sys_unstable_apis"] + +# Enable for testing WASM-only stuff +[build] +target = "wasm32-unknown-unknown" diff --git a/Cargo.toml b/Cargo.toml index 4ce73fd..fa308ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ encoding_rs = "0.8.33" nofmt = "1.0.0" once_cell = "1.18.0" unicode-normalization = "0.1.22" -regex = "1.10.2" +regex = "1.10" cross_usb = "0.3" num-derive = "0.3.3" num-traits = "0.2.14" @@ -39,9 +39,10 @@ tokio = { version = "1.36", features = ["sync"] } g2-unicode-jp = "0.4.1" thiserror = "1.0.57" phf = { version = "0.11.2", features = ["phf_macros", "macros"] } +byteorder = "1.5.0" [target.'cfg(target_family = "wasm")'.dependencies] -gloo = { version = "0.11.0", features = ["futures"] } +gloo = { version = "0.11.0", features = ["futures", "worker"] } [package.metadata.wasm-pack.profile.dev.wasm-bindgen] dwarf-debug-info = true diff --git a/src/netmd/base.rs b/src/netmd/base.rs index c35be95..d76aeaa 100644 --- a/src/netmd/base.rs +++ b/src/netmd/base.rs @@ -1,7 +1,6 @@ #![cfg_attr(debug_assertions, allow(dead_code))] use std::time::Duration; -use nofmt; use once_cell::sync::Lazy; use thiserror::Error; @@ -15,58 +14,54 @@ use super::utils::cross_sleep; const BULK_WRITE_ENDPOINT: u8 = 0x02; const BULK_READ_ENDPOINT: u8 = 0x81; -pub static DEVICE_IDS: Lazy> = Lazy::new(|| { - nofmt::pls! { - Box::new([ - DeviceId {vendor_id: 0x04dd, product_id: 0x7202, name: Some(String::from("Sharp IM-MT899H"))}, - DeviceId {vendor_id: 0x04dd, product_id: 0x9013, name: Some(String::from("Sharp IM-DR400"))}, - DeviceId {vendor_id: 0x04dd, product_id: 0x9014, name: Some(String::from("Sharp IM-DR80"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0034, name: Some(String::from("Sony PCLK-XX"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0036, name: Some(String::from("Sony"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0075, name: Some(String::from("Sony MZ-N1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x007c, name: Some(String::from("Sony"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0080, name: Some(String::from("Sony LAM-1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0081, name: Some(String::from("Sony MDS-JB980/MDS-NT1/MDS-JE780"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0084, name: Some(String::from("Sony MZ-N505"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0085, name: Some(String::from("Sony MZ-S1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0086, name: Some(String::from("Sony MZ-N707"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x008e, name: Some(String::from("Sony CMT-C7NT"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0097, name: Some(String::from("Sony PCGA-MDN1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00ad, name: Some(String::from("Sony CMT-L7HD"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00c6, name: Some(String::from("Sony MZ-N10"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00c7, name: Some(String::from("Sony MZ-N910"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00c8, name: Some(String::from("Sony MZ-N710/NF810"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00c9, name: Some(String::from("Sony MZ-N510/N610"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00ca, name: Some(String::from("Sony MZ-NE410/NF520D"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00e7, name: Some(String::from("Sony CMT-M333NT/M373NT"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x00eb, name: Some(String::from("Sony MZ-NE810/NE910"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0101, name: Some(String::from("Sony LAM"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0113, name: Some(String::from("Aiwa AM-NX1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x013f, name: Some(String::from("Sony MDS-S500"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x014c, name: Some(String::from("Aiwa AM-NX9"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x017e, name: Some(String::from("Sony MZ-NH1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0180, name: Some(String::from("Sony MZ-NH3D"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0182, name: Some(String::from("Sony MZ-NH900"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0184, name: Some(String::from("Sony MZ-NH700/NH800"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0186, name: Some(String::from("Sony MZ-NH600"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0187, name: Some(String::from("Sony MZ-NH600D"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0188, name: Some(String::from("Sony MZ-N920"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x018a, name: Some(String::from("Sony LAM-3"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x01e9, name: Some(String::from("Sony MZ-DH10P"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0219, name: Some(String::from("Sony MZ-RH10"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x021b, name: Some(String::from("Sony MZ-RH710/MZ-RH910"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x021d, name: Some(String::from("Sony CMT-AH10"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x022c, name: Some(String::from("Sony CMT-AH10"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x023c, name: Some(String::from("Sony DS-HMD1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0286, name: Some(String::from("Sony MZ-RH1"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x011a, name: Some(String::from("Sony CMT-SE7"))}, - DeviceId {vendor_id: 0x054c, product_id: 0x0148, name: Some(String::from("Sony MDS-A1"))}, - DeviceId {vendor_id: 0x0b28, product_id: 0x1004, name: Some(String::from("Kenwood MDX-J9"))}, - DeviceId {vendor_id: 0x04da, product_id: 0x23b3, name: Some(String::from("Panasonic SJ-MR250"))}, - DeviceId {vendor_id: 0x04da, product_id: 0x23b6, name: Some(String::from("Panasonic SJ-MR270"))}, - ]) - } -}); +pub static DEVICE_IDS: &[DeviceId] = &[ + DeviceId {vendor_id: 0x04dd, product_id: 0x7202, name: Some("Sharp IM-MT899H")}, + DeviceId {vendor_id: 0x04dd, product_id: 0x9013, name: Some("Sharp IM-DR400")}, + DeviceId {vendor_id: 0x04dd, product_id: 0x9014, name: Some("Sharp IM-DR80")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0034, name: Some("Sony PCLK-XX")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0036, name: Some("Sony")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0075, name: Some("Sony MZ-N1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x007c, name: Some("Sony")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0080, name: Some("Sony LAM-1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0081, name: Some("Sony MDS-JB980/MDS-NT1/MDS-JE780")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0084, name: Some("Sony MZ-N505")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0085, name: Some("Sony MZ-S1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0086, name: Some("Sony MZ-N707")}, + DeviceId {vendor_id: 0x054c, product_id: 0x008e, name: Some("Sony CMT-C7NT")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0097, name: Some("Sony PCGA-MDN1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00ad, name: Some("Sony CMT-L7HD")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00c6, name: Some("Sony MZ-N10")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00c7, name: Some("Sony MZ-N910")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00c8, name: Some("Sony MZ-N710/NF810")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00c9, name: Some("Sony MZ-N510/N610")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00ca, name: Some("Sony MZ-NE410/NF520D")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00e7, name: Some("Sony CMT-M333NT/M373NT")}, + DeviceId {vendor_id: 0x054c, product_id: 0x00eb, name: Some("Sony MZ-NE810/NE910")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0101, name: Some("Sony LAM")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0113, name: Some("Aiwa AM-NX1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x013f, name: Some("Sony MDS-S500")}, + DeviceId {vendor_id: 0x054c, product_id: 0x014c, name: Some("Aiwa AM-NX9")}, + DeviceId {vendor_id: 0x054c, product_id: 0x017e, name: Some("Sony MZ-NH1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0180, name: Some("Sony MZ-NH3D")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0182, name: Some("Sony MZ-NH900")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0184, name: Some("Sony MZ-NH700/NH800")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0186, name: Some("Sony MZ-NH600")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0187, name: Some("Sony MZ-NH600D")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0188, name: Some("Sony MZ-N920")}, + DeviceId {vendor_id: 0x054c, product_id: 0x018a, name: Some("Sony LAM-3")}, + DeviceId {vendor_id: 0x054c, product_id: 0x01e9, name: Some("Sony MZ-DH10P")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0219, name: Some("Sony MZ-RH10")}, + DeviceId {vendor_id: 0x054c, product_id: 0x021b, name: Some("Sony MZ-RH710/MZ-RH910")}, + DeviceId {vendor_id: 0x054c, product_id: 0x021d, name: Some("Sony CMT-AH10")}, + DeviceId {vendor_id: 0x054c, product_id: 0x022c, name: Some("Sony CMT-AH10")}, + DeviceId {vendor_id: 0x054c, product_id: 0x023c, name: Some("Sony DS-HMD1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0286, name: Some("Sony MZ-RH1")}, + DeviceId {vendor_id: 0x054c, product_id: 0x011a, name: Some("Sony CMT-SE7")}, + DeviceId {vendor_id: 0x054c, product_id: 0x0148, name: Some("Sony MDS-A1")}, + DeviceId {vendor_id: 0x0b28, product_id: 0x1004, name: Some("Kenwood MDX-J9")}, + DeviceId {vendor_id: 0x04da, product_id: 0x23b3, name: Some("Panasonic SJ-MR250")}, + DeviceId {vendor_id: 0x04da, product_id: 0x23b6, name: Some("Panasonic SJ-MR270")}, +]; pub static DEVICE_IDS_CROSSUSB: Lazy> = Lazy::new(|| { DEVICE_IDS.iter().map(|d|{ @@ -94,7 +89,7 @@ pub enum Status { pub struct DeviceId { vendor_id: u16, product_id: u16, - name: Option, + name: Option<&'static str>, } #[derive(Error, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -156,8 +151,8 @@ impl NetMD { } /// Gets the device name, this is limited to the devices in the list - pub fn device_name(&self) -> &Option { - &self.model.name + pub fn device_name(&self) -> Option<&str> { + self.model.name } /// Gets the vendor id @@ -310,20 +305,22 @@ impl NetMD { } // Default chunksize should be 0x10000 - pub async fn read_bulk( + pub async fn read_bulk( &mut self, length: usize, chunksize: usize, + progress_callback: Option, ) -> Result, NetMDError> { - let result = self.read_bulk_to_array(length, chunksize).await?; + let result = self.read_bulk_to_array(length, chunksize, progress_callback).await?; Ok(result) } - pub async fn read_bulk_to_array( + pub async fn read_bulk_to_array( &mut self, length: usize, chunksize: usize, + progress_callback: Option, ) -> Result, NetMDError> { let mut final_result: Vec = Vec::new(); let mut done = 0; @@ -341,6 +338,10 @@ impl NetMD { Err(error) => return Err(NetMDError::UsbError(error)), }; + if let Some(cb) = &progress_callback { + cb(length, done) + } + final_result.extend_from_slice(&res); } diff --git a/src/netmd/commands.rs b/src/netmd/commands.rs index 3ef459d..1d6eb9c 100644 --- a/src/netmd/commands.rs +++ b/src/netmd/commands.rs @@ -1,15 +1,16 @@ #![cfg_attr(debug_assertions, allow(dead_code))] use num_derive::FromPrimitive; use num_traits::FromPrimitive; +use regex::Regex; use std::error::Error; use std::time::Duration; use cross_usb::Descriptor; use crate::netmd::interface::DiscFlag; -use crate::netmd::utils::RawTime; +use crate::netmd::utils::{create_aea_header, create_wav_header, AeaOptions, RawTime}; -use super::interface::{Channels, Encoding, InterfaceError, MDSession, MDTrack, NetMDInterface, TrackFlag}; -use super::utils::cross_sleep; +use super::interface::{Channels, Direction, DiscFormat, Encoding, InterfaceError, MDSession, MDTrack, NetMDInterface, TrackFlag}; +use super::utils::{cross_sleep, half_width_title_length, half_width_to_full_width_range, sanitize_full_width_title, sanitize_half_width_title}; #[derive(FromPrimitive, PartialEq, Eq)] pub enum OperatingStatus { @@ -49,17 +50,19 @@ pub struct Track { } impl Track { - pub fn chars_to_cells(len: usize) -> usize { - f32::ceil(len as f32 / 7.0) as usize - } - - pub async fn cells_for_title(&mut self) { + pub fn cells_for_title(&self) -> (usize, usize) { let encoding_name_correction = match self.encoding { Encoding::SP => 0, _ => 1 }; - let full_width_length = Self::chars_to_cells(self.full_width_title.len() * 2); + let full_width_length = chars_to_cells(self.full_width_title.len() * 2); + let half_width_length = chars_to_cells(half_width_title_length(&self.title)); + + ( + usize::max(encoding_name_correction, half_width_length), + usize::max(encoding_name_correction, full_width_length), + ) } } @@ -83,18 +86,142 @@ pub struct Disc { } impl Disc { - pub async fn track_count(&self) -> u16 { + pub fn track_count(&self) -> u16 { self.groups.iter() .map(|g| g.tracks.len()) .reduce(|acc, s| acc + s) .unwrap() as u16 } - pub async fn tracks(&self) -> Vec { + pub fn tracks(&self) -> Vec { self.groups.iter() .flat_map(|g| g.tracks.clone()) .collect() } + + fn remaining_characters_for_titles(&self, ignore_disc_titles: bool, include_groups: bool) -> (usize, usize) { + const CELL_LIMIT: usize = 255; + + let groups = self.groups.iter().filter(|g| g.title.is_some()); + + let (disc_fw_title, disc_hw_title) = if !ignore_disc_titles { + (self.full_width_title.clone(), self.full_width_title.clone()) + } else { + (String::new(), String::new()) + }; + + let mut fw_title = disc_fw_title + "0;//"; + let mut hw_title = disc_hw_title + "0;//"; + + if include_groups { + for group in groups { + let indices: Vec = group.tracks.iter().map(|t| t.index).collect(); + let min_group_index = indices.iter().min().unwrap(); + let max_group_index = indices.iter().max().unwrap(); + + let range = format!( + "{}{}", + min_group_index + 1, + { + if group.tracks.len() - 1 != 0 { + format!("-{}", max_group_index + 1) + } else { + String::from("") + } + } + ); + + fw_title.push_str((group.full_width_title.clone().unwrap() + &range).as_str()); + hw_title.push_str((group.title.clone().unwrap() + &range).as_str()); + } + } + + let mut used_half_width_cells = 0; + let mut used_full_width_cells = 0; + + used_full_width_cells += chars_to_cells(fw_title.len() * 2); + used_half_width_cells += chars_to_cells(half_width_title_length(&hw_title)); + + for track in self.tracks() { + let (half_width, full_width) = track.cells_for_title(); + used_half_width_cells += half_width; + used_full_width_cells += full_width; + } + + ( + usize::max(CELL_LIMIT - used_full_width_cells, 0) * 7, + usize::max(CELL_LIMIT - used_half_width_cells, 0) * 7 + ) + } + + pub fn compile_disc_titles(&self) -> (String, String) { + let (available_full_width, available_half_width) = self.remaining_characters_for_titles(true, false); + + let use_full_width = + self.groups.iter().filter(|n| n.full_width_title.as_ref().is_some_and(|t| !t.is_empty())).count() > 0 || + self.tracks().iter().filter(|t| !t.full_width_title.is_empty()).count() > 0; + + let mut new_raw_title = String::new(); + let mut new_raw_full_width_title = String::new(); + + if !self.title.is_empty() { + new_raw_title = format!("0;{}//", self.title); + } + if use_full_width { + new_raw_full_width_title = format!("0;{}//", self.full_width_title); + } + + for group in &self.groups { + if group.title.is_none() || group.tracks.is_empty() { + continue; + } + + let min_group_index = group.tracks.iter().map(|t| t.index).min().unwrap_or_default(); + let mut range = format!("{}", min_group_index + 1); + + if group.tracks.len() != 1 { + range.push_str(&format!("-{}", min_group_index as usize + group.tracks.len())); + } + + let new_raw_title_after_group = new_raw_title.clone() + &format!("{};{}//", range, group.title.as_ref().unwrap()); + let new_raw_full_width_title_after_group = + new_raw_full_width_title.clone() + + &half_width_to_full_width_range(&range) + + &format!(";{}//", group.full_width_title.as_ref().unwrap_or(&String::new())); + + let half_width_titles_length_in_toc = chars_to_cells(half_width_title_length(&new_raw_title_after_group)); + + if use_full_width { + let full_width_titles_length_in_toc = chars_to_cells(new_raw_full_width_title_after_group.len() * 2) * 7; + if available_full_width as isize - full_width_titles_length_in_toc as isize >= 0 { + new_raw_full_width_title = new_raw_full_width_title_after_group + } + } + + if available_half_width as isize - half_width_titles_length_in_toc as isize >= 0 { + new_raw_title = new_raw_title_after_group + } + } + + let half_width_titles_length_in_toc = chars_to_cells(half_width_title_length(&new_raw_title)) * 7; + let full_width_titles_length_in_toc = chars_to_cells(new_raw_full_width_title.len() * 2); + + if (available_half_width as isize - half_width_titles_length_in_toc as isize) < 0 { + new_raw_title = String::new(); + } + if (available_full_width as isize - full_width_titles_length_in_toc as isize) < 0 { + new_raw_full_width_title = String::new(); + } + + ( + new_raw_title, + if use_full_width { + new_raw_full_width_title + } else { + String::new() + } + ) + } } pub struct NetMDContext { @@ -111,7 +238,6 @@ impl NetMDContext { }) } - /* /// Change to the next track (skip forward) pub async fn next_track(&mut self) -> Result<(), InterfaceError> { self.interface.track_change(Direction::Next).await @@ -126,7 +252,6 @@ impl NetMDContext { pub async fn restart_track(&mut self) -> Result<(), InterfaceError> { self.interface.track_change(Direction::Restart).await } - */ pub async fn device_status(&mut self) -> Result> { let status = self.interface.status().await?; @@ -158,46 +283,6 @@ impl NetMDContext { }) } - pub async fn prepare_download(&mut self) -> Result<(), Box> { - while ![OperatingStatus::DiscBlank, OperatingStatus::Ready].contains( - &self.device_status() - .await? - .state - .unwrap_or(OperatingStatus::NoDisc), - ) { - cross_sleep(Duration::from_millis(200)).await; - } - - let _ = self.interface.session_key_forget().await; - let _ = self.interface.leave_secure_session().await; - - self.interface.acquire().await?; - let _ = self.interface.disable_new_track_protection(1).await; - - Ok(()) - } - - pub async fn download( - &mut self, - track: MDTrack, - progress_callback: F, - ) -> Result<(u16, Vec, Vec), Box> - where - F: Fn(usize, usize), - { - self.prepare_download().await?; - // Lock the interface by providing it to the session - let mut session = MDSession::new(&mut self.interface); - session.init().await?; - let result = session - .download_track(track, progress_callback, None) - .await?; - session.close().await?; - self.interface.release().await?; - - Ok(result) - } - pub async fn list_content(&mut self) -> Result> { let flags = self.interface.disc_flags().await?; let title = self.interface.disc_title(false).await?; @@ -265,4 +350,157 @@ impl NetMDContext { Ok(disc) } + + pub async fn rewrite_disc_groups(&mut self, disc: Disc) -> Result<(), Box> { + let (new_raw_title, new_raw_full_width_title) = disc.compile_disc_titles(); + + self.interface.set_disc_title(&new_raw_title, false).await?; + self.interface.set_disc_title(&new_raw_full_width_title, false).await?; + + Ok(()) + } + + pub async fn rename_disc(&mut self, new_name: &str, new_fw_name: Option<&str>) -> Result<(), Box> { + let new_name = sanitize_half_width_title(new_name); + let new_fw_name = if let Some(name) = new_fw_name { + Some(sanitize_full_width_title(name)) + } else { + None + }; + + let old_name = self.interface.disc_title(false).await?; + let old_fw_name = self.interface.disc_title(true).await?; + let old_raw_name = self.interface.raw_disc_title(false).await?; + let old_raw_fw_name = self.interface.raw_disc_title(true).await?; + + let has_groups = old_raw_name.find("//").is_some(); + let has_fw_groups = old_raw_fw_name.find("//").is_some(); + + let has_groups_and_title = old_raw_name.starts_with("0;"); + let has_fw_groups_and_title = old_raw_fw_name.starts_with("0;"); + + if new_fw_name.as_ref().is_some_and(|n| n != &old_fw_name) { + let new_fw_name_with_groups; + if has_fw_groups { + if has_fw_groups_and_title { + let re = Regex::new("/^0;.*?///").unwrap(); + new_fw_name_with_groups = re.replace_all( + &old_raw_fw_name, + if !new_fw_name.as_ref().unwrap().is_empty() { + format!("0;{}//", new_fw_name.unwrap()) + } else { + String::new() + } + ).into() + } else { + new_fw_name_with_groups = format!(r"0;{}//{}", new_fw_name.unwrap(), old_raw_fw_name); + } + } else { + new_fw_name_with_groups = new_fw_name.unwrap(); + } + + self.interface.set_disc_title(&new_fw_name_with_groups, true).await?; + } + + if new_name == old_name { + return Ok(()) + } + + let new_name_with_groups; + if has_groups { + if has_groups_and_title { + let re = Regex::new(r"/^0;.*?\/\//").unwrap(); + new_name_with_groups = re.replace_all( + &old_raw_name, + if !new_name.is_empty() { + format!("0;{}//", new_name) + } else { + String::new() + } + ).into() + } else { + new_name_with_groups = format!("0;{}//{}", new_name, old_raw_name); + } + } else { + new_name_with_groups = new_name + } + + self.interface.set_disc_title(&new_name_with_groups, false).await?; + + Ok(()) + } + + pub async fn upload( + &mut self, + track: u16, + progress_callback: Option + ) -> Result<(DiscFormat, Vec), Box> { + let mut output_vec = Vec::new(); + let (format, _frames, result) = self.interface.save_track_to_array(track, progress_callback).await?; + + let header; + match format { + DiscFormat::SPMono | DiscFormat::SPStereo => { + let aea_options = AeaOptions { + name: &self.interface.track_title(track, false).await?, + channels: if format == DiscFormat::SPStereo { 2 } else { 1 }, + sound_groups: f32::floor(result.len() as f32 / 212.0) as u32, + ..Default::default() + }; + header = create_aea_header(aea_options); + }, + DiscFormat::LP2 | DiscFormat::LP4 => { + header = create_wav_header(format, result.len() as u32); + }, + } + + output_vec.extend_from_slice(&header); + output_vec.extend_from_slice(&result); + + Ok((format, header)) + } + + pub async fn prepare_download(&mut self) -> Result<(), Box> { + while ![OperatingStatus::DiscBlank, OperatingStatus::Ready].contains( + &self.device_status() + .await? + .state + .unwrap_or(OperatingStatus::NoDisc), + ) { + cross_sleep(Duration::from_millis(200)).await; + } + + let _ = self.interface.session_key_forget().await; + let _ = self.interface.leave_secure_session().await; + + self.interface.acquire().await?; + let _ = self.interface.disable_new_track_protection(1).await; + + Ok(()) + } + + pub async fn download( + &mut self, + track: MDTrack, + progress_callback: F, + ) -> Result<(u16, Vec, Vec), Box> + where + F: Fn(usize, usize), + { + self.prepare_download().await?; + // Lock the interface by providing it to the session + let mut session = MDSession::new(&mut self.interface); + session.init().await?; + let result = session + .download_track(track, progress_callback, None) + .await?; + session.close().await?; + self.interface.release().await?; + + Ok(result) + } +} + +fn chars_to_cells(len: usize) -> usize { + f32::ceil(len as f32 / 7.0) as usize } diff --git a/src/netmd/encryption.rs b/src/netmd/encryption.rs index 285dc6c..d51bd1c 100644 --- a/src/netmd/encryption.rs +++ b/src/netmd/encryption.rs @@ -73,5 +73,7 @@ pub fn new_thread_encryptor( } }); + + rx } diff --git a/src/netmd/interface.rs b/src/netmd/interface.rs index e11dd4d..09bd860 100644 --- a/src/netmd/interface.rs +++ b/src/netmd/interface.rs @@ -18,7 +18,7 @@ use std::error::Error; use tokio::sync::mpsc::UnboundedReceiver; use super::base::NetMD; -use super::utils::cross_sleep; +use super::utils::{cross_sleep, to_sjis}; #[derive(Copy, Clone)] enum Action { @@ -34,7 +34,7 @@ pub enum Direction { Restart = 0x0001, } -#[derive(Debug, Clone, Copy, FromPrimitive)] +#[derive(Debug, Clone, Copy, FromPrimitive, PartialEq, Eq)] pub enum DiscFormat { LP4 = 0, LP2 = 2, @@ -846,7 +846,7 @@ impl NetMDInterface { } /// Gets the disc title as it is stored - async fn raw_disc_title(&mut self, wchar: bool) -> Result { + pub async fn raw_disc_title(&mut self, wchar: bool) -> Result { self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) .await?; self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenRead) @@ -1098,11 +1098,11 @@ impl NetMDInterface { let wchar_value = match wchar { true => { - new_title = sanitize_full_width_title(title, false); + new_title = to_sjis(&sanitize_full_width_title(title)); 1 } false => { - new_title = sanitize_half_width_title(title); + new_title = to_sjis(&sanitize_half_width_title(title)); 0 } }; @@ -1156,11 +1156,11 @@ impl NetMDInterface { let new_title: Vec; let (wchar_value, descriptor) = match wchar { true => { - new_title = sanitize_full_width_title(title, false); + new_title = to_sjis(&sanitize_full_width_title(title)); (3, Descriptor::AudioUTOC4TD) } false => { - new_title = sanitize_half_width_title(title); + new_title = to_sjis(&sanitize_half_width_title(title)); (2, Descriptor::AudioUTOC1TD) } }; @@ -1418,9 +1418,10 @@ impl NetMDInterface { /// Gets the bytes of a track /// /// This can only be executed on an MZ-RH1 / M200 - pub async fn save_track_to_array( + pub async fn save_track_to_array( &mut self, track: u16, + progress_callback: Option ) -> Result<(DiscFormat, u16, Vec), InterfaceError> { let mut query = format_query( "1800 080046 f003010330 ff00 1001 %w".to_string(), @@ -1438,14 +1439,14 @@ impl NetMDInterface { let codec = res[1].to_i64().unwrap() as u8; let length = res[2].to_i64().unwrap() as usize; - let result = self.device.read_bulk(length, 0x10000).await?; + let result = self.device.read_bulk(length, 0x10000, progress_callback).await?; scan_query( self.read_reply(false).await?, "1800 080046 f003010330 0000 1001 %?%? %?%?".to_string(), )?; - std::thread::sleep(std::time::Duration::from_millis(500)); + cross_sleep(Duration::from_millis(500)).await; let format: DiscFormat = match codec & 0x06 { 0 => DiscFormat::LP4, diff --git a/src/netmd/mappings.rs b/src/netmd/mappings.rs index 6ce22aa..435b866 100644 --- a/src/netmd/mappings.rs +++ b/src/netmd/mappings.rs @@ -1,265 +1,265 @@ use phf::phf_map; -pub static MAPPINGS_JP: phf::Map<&'static str, &'static str> = phf_map!{ - "!" =>"!", - "\"" =>""", - "#" =>"#", - "$" =>"$", - "%" =>"%", - "&" =>"&", - "'" =>"'", - "" =>"(", - ")" =>")", - "*" =>"*", - "+" =>"+", - "," =>",", - "-" =>"-", - "." =>".", - "/" =>"/", - ":" =>":", - ";" =>";", - "<" =>"<", - "=" =>"=", - ">" =>">", - "?" =>"?", - "@" =>"@", - "A" =>"A", - "B" =>"B", - "C" =>"C", - "D" =>"D", - "E" =>"E", - "F" =>"F", - "G" =>"G", - "H" =>"H", - "I" =>"I", - "J" =>"J", - "K" =>"K", - "L" =>"L", - "M" =>"M", - "N" =>"N", - "O" =>"O", - "P" =>"P", - "Q" =>"Q", - "R" =>"R", - "S" =>"S", - "T" =>"T", - "U" =>"U", - "V" =>"V", - "W" =>"W", - "X" =>"X", - "Y" =>"Y", - "Z" =>"Z", - "[" =>"[", - "\\" =>"\", - "]" =>"]", - "^" =>"^", - "_" =>"_", - "`" =>"`", - "a" =>"a", - "b" =>"b", - "c" =>"c", - "d" =>"d", - "e" =>"e", - "f" =>"f", - "g" =>"g", - "h" =>"h", - "i" =>"i", - "j" =>"j", - "k" =>"k", - "l" =>"l", - "m" =>"m", - "n" =>"n", - "o" =>"o", - "p" =>"p", - "q" =>"q", - "r" =>"r", - "s" =>"s", - "t" =>"t", - "u" =>"u", - "v" =>"v", - "w" =>"w", - "x" =>"x", - "y" =>"y", - "z" =>"z", - "{" =>"{", - "|" =>"|", - "}" =>"}", - "~" =>"~", - " " =>"\u{3000}", - "0" =>"0", - "1" =>"1", - "2" =>"2", - "3" =>"3", - "4" =>"4", - "5" =>"5", - "6" =>"6", - "7" =>"7", - "8" =>"8", - "9" =>"9", - "ァ" =>"ァ", - "ア" =>"ア", - "ィ" =>"ィ", - "イ" =>"イ", - "ゥ" =>"ゥ", - "ウ" =>"ウ", - "ェ" =>"ェ", - "エ" =>"エ", - "ォ" =>"ォ", - "オ" =>"オ", - "カ" =>"カ", - "ガ" =>"ガ", - "キ" =>"キ", - "ギ" =>"ギ", - "ク" =>"ク", - "グ" =>"グ", - "ケ" =>"ケ", - "ゲ" =>"ゲ", - "コ" =>"コ", - "ゴ" =>"ゴ", - "サ" =>"サ", - "ザ" =>"ザ", - "シ" =>"シ", - "ジ" =>"ジ", - "ス" =>"ス", - "ズ" =>"ズ", - "セ" =>"セ", - "ゼ" =>"ゼ", - "ソ" =>"ソ", - "ゾ" =>"ゾ", - "タ" =>"タ", - "ダ" =>"ダ", - "チ" =>"チ", - "ヂ" =>"ヂ", - "ッ" =>"ッ", - "ツ" =>"ツ", - "ヅ" =>"ヅ", - "テ" =>"テ", - "デ" =>"デ", - "ト" =>"ト", - "ド" =>"ド", - "ナ" =>"ナ", - "ニ" =>"ニ", - "ヌ" =>"ヌ", - "ネ" =>"ネ", - "ノ" =>"ノ", - "ハ" =>"ハ", - "バ" =>"バ", - "パ" =>"パ", - "ヒ" =>"ヒ", - "ビ" =>"ビ", - "ピ" =>"ピ", - "フ" =>"フ", - "ブ" =>"ブ", - "プ" =>"プ", - "ヘ" =>"ヘ", - "ベ" =>"ベ", - "ペ" =>"ペ", - "ホ" =>"ホ", - "ボ" =>"ボ", - "ポ" =>"ポ", - "マ" =>"マ", - "ミ" =>"ミ", - "ム" =>"ム", - "メ" =>"メ", - "モ" =>"モ", - "ャ" =>"ャ", - "ヤ" =>"ヤ", - "ュ" =>"ュ", - "ユ" =>"ユ", - "ョ" =>"ョ", - "ヨ" =>"ヨ", - "ラ" =>"ラ", - "リ" =>"リ", - "ル" =>"ル", - "レ" =>"レ", - "ロ" =>"ロ", - "ワ" =>"ワ", - "ヲ" =>"ヲ", - "ン" =>"ン", - "ー" =>"ー", - "ヮ" =>"ヮ", - "ヰ" =>"ヰ", - "ヱ" =>"ヱ", - "ヵ" =>"ヵ", - "ヶ" =>"ヶ", - "ヴ" =>"ヴ", - "ヽ" =>"ヽ", - "ヾ" =>"ヾ", - "・" =>"・", - "「" =>"「", - "」" =>"」", - "。" =>"。", - "、" =>"、" -}; -pub static MAPPINGS_RU: phf::Map<&'static str, &'static str> = phf_map!{ - "а" =>"a", - "б" =>"b", - "в" =>"v", - "г" =>"g", - "д" =>"d", - "е" =>"e", - "ё" =>"e", - "ж" =>"zh", - "з" =>"z", - "и" =>"i", - "й" =>"i", - "к" =>"k", - "л" =>"l", - "м" =>"m", - "н" =>"n", - "о" =>"o", - "п" =>"p", - "р" =>"r", - "с" =>"s", - "т" =>"t", - "у" =>"u", - "ф" =>"f", - "х" =>"kh", - "ц" =>"tc", - "ч" =>"ch", - "ш" =>"sh", - "щ" =>"shch", - "ъ" =>"", - "ы" =>"y", - "ь" =>"'", - "э" =>"e", - "ю" =>"iu", - "я" =>"ia", - "А" =>"A", - "Б" =>"B", - "В" =>"V", - "Г" =>"G", - "Д" =>"D", - "Е" =>"E", - "Ё" =>"E", - "Ж" =>"Zh", - "З" =>"Z", - "И" =>"I", - "Й" =>"I", - "К" =>"K", - "Л" =>"L", - "М" =>"M", - "Н" =>"N", - "О" =>"O", - "П" =>"P", - "Р" =>"R", - "С" =>"S", - "Т" =>"T", - "У" =>"U", - "Ф" =>"F", - "Х" =>"Kh", - "Ц" =>"Tc", - "Ч" =>"Ch", - "Ш" =>"Sh", - "Щ" =>"Shch", - "Ъ" =>"", - "Ы" =>"Y", - "Ь" =>"'", - "Э" =>"E", - "Ю" =>"Iu", - "Я" =>"Ia" -}; -pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map!{ +pub static MAPPINGS_JP: phf::Map<&'static str, &'static str> = phf_map![ + "!" =>"!", + "\"" =>""", + "#" =>"#", + "$" =>"$", + "%" =>"%", + "&" =>"&", + "'" =>"'", + "" =>"(", + ")" =>")", + "*" =>"*", + "+" =>"+", + "," =>",", + "-" =>"-", + "." =>".", + "/" =>"/", + ":" =>":", + ";" =>";", + "<" =>"<", + "=" =>"=", + ">" =>">", + "?" =>"?", + "@" =>"@", + "A" =>"A", + "B" =>"B", + "C" =>"C", + "D" =>"D", + "E" =>"E", + "F" =>"F", + "G" =>"G", + "H" =>"H", + "I" =>"I", + "J" =>"J", + "K" =>"K", + "L" =>"L", + "M" =>"M", + "N" =>"N", + "O" =>"O", + "P" =>"P", + "Q" =>"Q", + "R" =>"R", + "S" =>"S", + "T" =>"T", + "U" =>"U", + "V" =>"V", + "W" =>"W", + "X" =>"X", + "Y" =>"Y", + "Z" =>"Z", + "[" =>"[", + "\\" =>"\", + "]" =>"]", + "^" =>"^", + "_" =>"_", + "`" =>"`", + "a" =>"a", + "b" =>"b", + "c" =>"c", + "d" =>"d", + "e" =>"e", + "f" =>"f", + "g" =>"g", + "h" =>"h", + "i" =>"i", + "j" =>"j", + "k" =>"k", + "l" =>"l", + "m" =>"m", + "n" =>"n", + "o" =>"o", + "p" =>"p", + "q" =>"q", + "r" =>"r", + "s" =>"s", + "t" =>"t", + "u" =>"u", + "v" =>"v", + "w" =>"w", + "x" =>"x", + "y" =>"y", + "z" =>"z", + "{" =>"{", + "|" =>"|", + "}" =>"}", + "~" =>"~", + " " =>"\u{3000}", + "0" =>"0", + "1" =>"1", + "2" =>"2", + "3" =>"3", + "4" =>"4", + "5" =>"5", + "6" =>"6", + "7" =>"7", + "8" =>"8", + "9" =>"9", + "ァ" =>"ァ", + "ア" =>"ア", + "ィ" =>"ィ", + "イ" =>"イ", + "ゥ" =>"ゥ", + "ウ" =>"ウ", + "ェ" =>"ェ", + "エ" =>"エ", + "ォ" =>"ォ", + "オ" =>"オ", + "カ" =>"カ", + "ガ" =>"ガ", + "キ" =>"キ", + "ギ" =>"ギ", + "ク" =>"ク", + "グ" =>"グ", + "ケ" =>"ケ", + "ゲ" =>"ゲ", + "コ" =>"コ", + "ゴ" =>"ゴ", + "サ" =>"サ", + "ザ" =>"ザ", + "シ" =>"シ", + "ジ" =>"ジ", + "ス" =>"ス", + "ズ" =>"ズ", + "セ" =>"セ", + "ゼ" =>"ゼ", + "ソ" =>"ソ", + "ゾ" =>"ゾ", + "タ" =>"タ", + "ダ" =>"ダ", + "チ" =>"チ", + "ヂ" =>"ヂ", + "ッ" =>"ッ", + "ツ" =>"ツ", + "ヅ" =>"ヅ", + "テ" =>"テ", + "デ" =>"デ", + "ト" =>"ト", + "ド" =>"ド", + "ナ" =>"ナ", + "ニ" =>"ニ", + "ヌ" =>"ヌ", + "ネ" =>"ネ", + "ノ" =>"ノ", + "ハ" =>"ハ", + "バ" =>"バ", + "パ" =>"パ", + "ヒ" =>"ヒ", + "ビ" =>"ビ", + "ピ" =>"ピ", + "フ" =>"フ", + "ブ" =>"ブ", + "プ" =>"プ", + "ヘ" =>"ヘ", + "ベ" =>"ベ", + "ペ" =>"ペ", + "ホ" =>"ホ", + "ボ" =>"ボ", + "ポ" =>"ポ", + "マ" =>"マ", + "ミ" =>"ミ", + "ム" =>"ム", + "メ" =>"メ", + "モ" =>"モ", + "ャ" =>"ャ", + "ヤ" =>"ヤ", + "ュ" =>"ュ", + "ユ" =>"ユ", + "ョ" =>"ョ", + "ヨ" =>"ヨ", + "ラ" =>"ラ", + "リ" =>"リ", + "ル" =>"ル", + "レ" =>"レ", + "ロ" =>"ロ", + "ワ" =>"ワ", + "ヲ" =>"ヲ", + "ン" =>"ン", + "ー" =>"ー", + "ヮ" =>"ヮ", + "ヰ" =>"ヰ", + "ヱ" =>"ヱ", + "ヵ" =>"ヵ", + "ヶ" =>"ヶ", + "ヴ" =>"ヴ", + "ヽ" =>"ヽ", + "ヾ" =>"ヾ", + "・" =>"・", + "「" =>"「", + "」" =>"」", + "。" =>"。", + "、" =>"、" +]; +pub static MAPPINGS_RU: phf::Map<&'static str, &'static str> = phf_map![ + "а" =>"a", + "б" =>"b", + "в" =>"v", + "г" =>"g", + "д" =>"d", + "е" =>"e", + "ё" =>"e", + "ж" =>"zh", + "з" =>"z", + "и" =>"i", + "й" =>"i", + "к" =>"k", + "л" =>"l", + "м" =>"m", + "н" =>"n", + "о" =>"o", + "п" =>"p", + "р" =>"r", + "с" =>"s", + "т" =>"t", + "у" =>"u", + "ф" =>"f", + "х" =>"kh", + "ц" =>"tc", + "ч" =>"ch", + "ш" =>"sh", + "щ" =>"shch", + "ъ" =>"", + "ы" =>"y", + "ь" =>"'", + "э" =>"e", + "ю" =>"iu", + "я" =>"ia", + "А" =>"A", + "Б" =>"B", + "В" =>"V", + "Г" =>"G", + "Д" =>"D", + "Е" =>"E", + "Ё" =>"E", + "Ж" =>"Zh", + "З" =>"Z", + "И" =>"I", + "Й" =>"I", + "К" =>"K", + "Л" =>"L", + "М" =>"M", + "Н" =>"N", + "О" =>"O", + "П" =>"P", + "Р" =>"R", + "С" =>"S", + "Т" =>"T", + "У" =>"U", + "Ф" =>"F", + "Х" =>"Kh", + "Ц" =>"Tc", + "Ч" =>"Ch", + "Ш" =>"Sh", + "Щ" =>"Shch", + "Ъ" =>"", + "Ы" =>"Y", + "Ь" =>"'", + "Э" =>"E", + "Ю" =>"Iu", + "Я" =>"Ia" +]; +pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map![ "Ä" => "Ae", "ä" => "ae", "Ö" => "Oe", @@ -267,564 +267,385 @@ pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map!{ "Ü" => "Ue", "ü" => "ue", "ß" => "ss" -}; -pub static MAPPINGS_HW: phf::Map<&'static str, &'static str> = phf_map!{ - "-" =>"-", - "ー" =>"-", - "ァ" =>"ァ", - "ア" =>"ア", - "ィ" =>"ィ", - "イ" =>"イ", - "ゥ" =>"ゥ", - "ウ" =>"ウ", - "ェ" =>"ェ", - "エ" =>"エ", - "ォ" =>"ォ", - "オ" =>"オ", - "カ" =>"カ", - "ガ" =>"ガ", - "キ" =>"キ", - "ギ" =>"ギ", - "ク" =>"ク", - "グ" =>"グ", - "ケ" =>"ケ", - "ゲ" =>"ゲ", - "コ" =>"コ", - "ゴ" =>"ゴ", - "サ" =>"サ", - "ザ" =>"ザ", - "シ" =>"シ", - "ジ" =>"ジ", - "ス" =>"ス", - "ズ" =>"ズ", - "セ" =>"セ", - "ゼ" =>"ゼ", - "ソ" =>"ソ", - "ゾ" =>"ゾ", - "タ" =>"タ", - "ダ" =>"ダ", - "チ" =>"チ", - "ヂ" =>"ヂ", - "ッ" =>"ッ", - "ツ" =>"ツ", - "ヅ" =>"ヅ", - "テ" =>"テ", - "デ" =>"デ", - "ト" =>"ト", - "ド" =>"ド", - "ナ" =>"ナ", - "ニ" =>"ニ", - "ヌ" =>"ヌ", - "ネ" =>"ネ", - "ノ" =>"ノ", - "ハ" =>"ハ", - "バ" =>"バ", - "パ" =>"パ", - "ヒ" =>"ヒ", - "ビ" =>"ビ", - "ピ" =>"ピ", - "フ" =>"フ", - "ブ" =>"ブ", - "プ" =>"プ", - "ヘ" =>"ヘ", - "ベ" =>"ベ", - "ペ" =>"ペ", - "ホ" =>"ホ", - "ボ" =>"ボ", - "ポ" =>"ポ", - "マ" =>"マ", - "ミ" =>"ミ", - "ム" =>"ム", - "メ" =>"メ", - "モ" =>"モ", - "ャ" =>"ャ", - "ヤ" =>"ヤ", - "ュ" =>"ュ", - "ユ" =>"ユ", - "ョ" =>"ョ", - "ヨ" =>"ヨ", - "ラ" =>"ラ", - "リ" =>"リ", - "ル" =>"ル", - "レ" =>"レ", - "ロ" =>"ロ", - "ワ" =>"ワ", - "ヲ" =>"ヲ", - "ン" =>"ン", - "ー" =>"-", - "ヮ" =>"ヮ", - "ヰ" =>"ヰ", - "ヱ" =>"ヱ", - "ヵ" =>"ヵ", - "ヶ" =>"ヶ", - "ヴ" =>"ヴ", - "ヽ" =>"ヽ", - "ヾ" =>"ヾ", - "・" =>"・", - "「" =>"「", - "」" =>"」", - "。" =>"。", - "、" =>"、", - "!" =>"!", - """ =>"\"", - "#" =>"#", - "$" =>"$", - "%" =>"%", - "&" =>"&", - "'" =>"'", - "(" =>"", - ")" =>")", - "*" =>"*", - "+" =>"+", - "," =>",", - "." =>".", - "/" =>"/", - ":" =>":", - ";" =>";", - "<" =>"<", - "=" =>"=", - ">" =>">", - "?" =>"?", - "@" =>"@", - "A" =>"A", - "B" =>"B", - "C" =>"C", - "D" =>"D", - "E" =>"E", - "F" =>"F", - "G" =>"G", - "H" =>"H", - "I" =>"I", - "J" =>"J", - "K" =>"K", - "L" =>"L", - "M" =>"M", - "N" =>"N", - "O" =>"O", - "P" =>"P", - "Q" =>"Q", - "R" =>"R", - "S" =>"S", - "T" =>"T", - "U" =>"U", - "V" =>"V", - "W" =>"W", - "X" =>"X", - "Y" =>"Y", - "Z" =>"Z", - "[" =>"[", - "\" =>"\\", - "]" =>"]", - "^" =>"^", - "_" =>"_", - "`" =>"`", - "a" =>"a", - "b" =>"b", - "c" =>"c", - "d" =>"d", - "e" =>"e", - "f" =>"f", - "g" =>"g", - "h" =>"h", - "i" =>"i", - "j" =>"j", - "k" =>"k", - "l" =>"l", - "m" =>"m", - "n" =>"n", - "o" =>"o", - "p" =>"p", - "q" =>"q", - "r" =>"r", - "s" =>"s", - "t" =>"t", - "u" =>"u", - "v" =>"v", - "w" =>"w", - "x" =>"x", - "y" =>"y", - "z" =>"z", - "{" =>"{", - "|" =>"|", - "}" =>"}", - "~" =>"~", - " " =>" ", - "0" =>"0", - "1" =>"1", - "2" =>"2", - "3" =>"3", - "4" =>"4", - "5" =>"5", - "6" =>"6", - "7" =>"7", - "8" =>"8", - "9" =>"9", - "ぁ" =>"ァ", - "あ" =>"ア", - "ぃ" =>"ィ", - "い" =>"イ", - "ぅ" =>"ゥ", - "う" =>"ウ", - "ぇ" =>"ェ", - "え" =>"エ", - "ぉ" =>"ォ", - "お" =>"オ", - "か" =>"カ", - "が" =>"ガ", - "き" =>"キ", - "ぎ" =>"ギ", - "く" =>"ク", - "ぐ" =>"グ", - "け" =>"ケ", - "げ" =>"ゲ", - "こ" =>"コ", - "ご" =>"ゴ", - "さ" =>"サ", - "ざ" =>"ザ", - "し" =>"シ", - "じ" =>"ジ", - "す" =>"ス", - "ず" =>"ズ", - "せ" =>"セ", - "ぜ" =>"ゼ", - "そ" =>"ソ", - "ぞ" =>"ゾ", - "た" =>"タ", - "だ" =>"ダ", - "ち" =>"チ", - "ぢ" =>"ヂ", - "っ" =>"ッ", - "つ" =>"ツ", - "づ" =>"ヅ", - "て" =>"テ", - "で" =>"デ", - "と" =>"ト", - "ど" =>"ド", - "な" =>"ナ", - "に" =>"ニ", - "ぬ" =>"ヌ", - "ね" =>"ネ", - "の" =>"ノ", - "は" =>"ハ", - "ば" =>"バ", - "ぱ" =>"パ", - "ひ" =>"ヒ", - "び" =>"ビ", - "ぴ" =>"ピ", - "ふ" =>"フ", - "ぶ" =>"ブ", - "ぷ" =>"プ", - "へ" =>"ヘ", - "べ" =>"ベ", - "ぺ" =>"ペ", - "ほ" =>"ホ", - "ぼ" =>"ボ", - "ぽ" =>"ポ", - "ま" =>"マ", - "み" =>"ミ", - "む" =>"ム", - "め" =>"メ", - "も" =>"モ", - "ゃ" =>"ャ", - "や" =>"ヤ", - "ゅ" =>"ュ", - "ゆ" =>"ユ", - "ょ" =>"ョ", - "よ" =>"ヨ", - "ら" =>"ラ", - "り" =>"リ", - "る" =>"ル", - "れ" =>"レ", - "ろ" =>"ロ", - "わ" =>"ワ", - "を" =>"ヲ", - "ん" =>"ン", - "ゎ" =>"ヮ", - "ゐ" =>"ヰ", - "ゑ" =>"ヱ", - "ゕ" =>"ヵ", - "ゖ" =>"ヶ", - "ゔ" =>"ヴ", - "ゝ" =>"ヽ", - "ゞ" =>"ヾ", -}; -pub static ALLOWED_HW_KANA: &[&'static str] = &[ - "-", - "-", - "ァ", - "ア", - "ィ", - "イ", - "ゥ", - "ウ", - "ェ", - "エ", - "ォ", - "オ", - "カ", - "ガ", - "キ", - "ギ", - "ク", - "グ", - "ケ", - "ゲ", - "コ", - "ゴ", - "サ", - "ザ", - "シ", - "ジ", - "ス", - "ズ", - "セ", - "ゼ", - "ソ", - "ゾ", - "タ", - "ダ", - "チ", - "ヂ", - "ッ", - "ツ", - "ヅ", - "テ", - "デ", - "ト", - "ド", - "ナ", - "ニ", - "ヌ", - "ネ", - "ノ", - "ハ", - "バ", - "パ", - "ヒ", - "ビ", - "ピ", - "フ", - "ブ", - "プ", - "ヘ", - "ベ", - "ペ", - "ホ", - "ボ", - "ポ", - "マ", - "ミ", - "ム", - "メ", - "モ", - "ャ", - "ヤ", - "ュ", - "ユ", - "ョ", - "ヨ", - "ラ", - "リ", - "ル", - "レ", - "ロ", - "ワ", - "ヲ", - "ン", - "-", - "ヮ", - "ヰ", - "ヱ", - "ヵ", - "ヶ", - "ヴ", - "ヽ", - "ヾ", - "・", - "「", - "」", - "。", - "、", - "!", - "\"", - "#", - "$", - "%", - "&", - "'", - "", - ")", - "*", - "+", - ",", - ".", - "/", - ":", - ";", - "<", - "=", - ">", - "?", - "@", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "[", - "\\", - "]", - "^", - "_", - "`", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "{", - "|", - "}", - "~", - " ", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "ァ", - "ア", - "ィ", - "イ", - "ゥ", - "ウ", - "ェ", - "エ", - "ォ", - "オ", - "カ", - "ガ", - "キ", - "ギ", - "ク", - "グ", - "ケ", - "ゲ", - "コ", - "ゴ", - "サ", - "ザ", - "シ", - "ジ", - "ス", - "ズ", - "セ", - "ゼ", - "ソ", - "ゾ", - "タ", - "ダ", - "チ", - "ヂ", - "ッ", - "ツ", - "ヅ", - "テ", - "デ", - "ト", - "ド", - "ナ", - "ニ", - "ヌ", - "ネ", - "ノ", - "ハ", - "バ", - "パ", - "ヒ", - "ビ", - "ピ", - "フ", - "ブ", - "プ", - "ヘ", - "ベ", - "ペ", - "ホ", - "ボ", - "ポ", - "マ", - "ミ", - "ム", - "メ", - "モ", - "ャ", - "ヤ", - "ュ", - "ユ", - "ョ", - "ヨ", - "ラ", - "リ", - "ル", - "レ", - "ロ", - "ワ", - "ヲ", - "ン", - "ヮ", - "ヰ", - "ヱ", - "ヵ", - "ヶ", - "ヴ", - "ヽ", - "ヾ", +]; +pub static MAPPINGS_HW: phf::Map<&'static str, &'static str> = phf_map![ + "-" =>"-", + "ー" =>"-", + "ァ" =>"ァ", + "ア" =>"ア", + "ィ" =>"ィ", + "イ" =>"イ", + "ゥ" =>"ゥ", + "ウ" =>"ウ", + "ェ" =>"ェ", + "エ" =>"エ", + "ォ" =>"ォ", + "オ" =>"オ", + "カ" =>"カ", + "ガ" =>"ガ", + "キ" =>"キ", + "ギ" =>"ギ", + "ク" =>"ク", + "グ" =>"グ", + "ケ" =>"ケ", + "ゲ" =>"ゲ", + "コ" =>"コ", + "ゴ" =>"ゴ", + "サ" =>"サ", + "ザ" =>"ザ", + "シ" =>"シ", + "ジ" =>"ジ", + "ス" =>"ス", + "ズ" =>"ズ", + "セ" =>"セ", + "ゼ" =>"ゼ", + "ソ" =>"ソ", + "ゾ" =>"ゾ", + "タ" =>"タ", + "ダ" =>"ダ", + "チ" =>"チ", + "ヂ" =>"ヂ", + "ッ" =>"ッ", + "ツ" =>"ツ", + "ヅ" =>"ヅ", + "テ" =>"テ", + "デ" =>"デ", + "ト" =>"ト", + "ド" =>"ド", + "ナ" =>"ナ", + "ニ" =>"ニ", + "ヌ" =>"ヌ", + "ネ" =>"ネ", + "ノ" =>"ノ", + "ハ" =>"ハ", + "バ" =>"バ", + "パ" =>"パ", + "ヒ" =>"ヒ", + "ビ" =>"ビ", + "ピ" =>"ピ", + "フ" =>"フ", + "ブ" =>"ブ", + "プ" =>"プ", + "ヘ" =>"ヘ", + "ベ" =>"ベ", + "ペ" =>"ペ", + "ホ" =>"ホ", + "ボ" =>"ボ", + "ポ" =>"ポ", + "マ" =>"マ", + "ミ" =>"ミ", + "ム" =>"ム", + "メ" =>"メ", + "モ" =>"モ", + "ャ" =>"ャ", + "ヤ" =>"ヤ", + "ュ" =>"ュ", + "ユ" =>"ユ", + "ョ" =>"ョ", + "ヨ" =>"ヨ", + "ラ" =>"ラ", + "リ" =>"リ", + "ル" =>"ル", + "レ" =>"レ", + "ロ" =>"ロ", + "ワ" =>"ワ", + "ヲ" =>"ヲ", + "ン" =>"ン", + "ー" =>"-", + "ヮ" =>"ヮ", + "ヰ" =>"ヰ", + "ヱ" =>"ヱ", + "ヵ" =>"ヵ", + "ヶ" =>"ヶ", + "ヴ" =>"ヴ", + "ヽ" =>"ヽ", + "ヾ" =>"ヾ", + "・" =>"・", + "「" =>"「", + "」" =>"」", + "。" =>"。", + "、" =>"、", + "!" =>"!", + """ =>"\"", + "#" =>"#", + "$" =>"$", + "%" =>"%", + "&" =>"&", + "'" =>"'", + "(" =>"", + ")" =>")", + "*" =>"*", + "+" =>"+", + "," =>",", + "." =>".", + "/" =>"/", + ":" =>":", + ";" =>";", + "<" =>"<", + "=" =>"=", + ">" =>">", + "?" =>"?", + "@" =>"@", + "A" =>"A", + "B" =>"B", + "C" =>"C", + "D" =>"D", + "E" =>"E", + "F" =>"F", + "G" =>"G", + "H" =>"H", + "I" =>"I", + "J" =>"J", + "K" =>"K", + "L" =>"L", + "M" =>"M", + "N" =>"N", + "O" =>"O", + "P" =>"P", + "Q" =>"Q", + "R" =>"R", + "S" =>"S", + "T" =>"T", + "U" =>"U", + "V" =>"V", + "W" =>"W", + "X" =>"X", + "Y" =>"Y", + "Z" =>"Z", + "[" =>"[", + "\" =>"\\", + "]" =>"]", + "^" =>"^", + "_" =>"_", + "`" =>"`", + "a" =>"a", + "b" =>"b", + "c" =>"c", + "d" =>"d", + "e" =>"e", + "f" =>"f", + "g" =>"g", + "h" =>"h", + "i" =>"i", + "j" =>"j", + "k" =>"k", + "l" =>"l", + "m" =>"m", + "n" =>"n", + "o" =>"o", + "p" =>"p", + "q" =>"q", + "r" =>"r", + "s" =>"s", + "t" =>"t", + "u" =>"u", + "v" =>"v", + "w" =>"w", + "x" =>"x", + "y" =>"y", + "z" =>"z", + "{" =>"{", + "|" =>"|", + "}" =>"}", + "~" =>"~", + " " =>" ", + "0" =>"0", + "1" =>"1", + "2" =>"2", + "3" =>"3", + "4" =>"4", + "5" =>"5", + "6" =>"6", + "7" =>"7", + "8" =>"8", + "9" =>"9", + "ぁ" =>"ァ", + "あ" =>"ア", + "ぃ" =>"ィ", + "い" =>"イ", + "ぅ" =>"ゥ", + "う" =>"ウ", + "ぇ" =>"ェ", + "え" =>"エ", + "ぉ" =>"ォ", + "お" =>"オ", + "か" =>"カ", + "が" =>"ガ", + "き" =>"キ", + "ぎ" =>"ギ", + "く" =>"ク", + "ぐ" =>"グ", + "け" =>"ケ", + "げ" =>"ゲ", + "こ" =>"コ", + "ご" =>"ゴ", + "さ" =>"サ", + "ざ" =>"ザ", + "し" =>"シ", + "じ" =>"ジ", + "す" =>"ス", + "ず" =>"ズ", + "せ" =>"セ", + "ぜ" =>"ゼ", + "そ" =>"ソ", + "ぞ" =>"ゾ", + "た" =>"タ", + "だ" =>"ダ", + "ち" =>"チ", + "ぢ" =>"ヂ", + "っ" =>"ッ", + "つ" =>"ツ", + "づ" =>"ヅ", + "て" =>"テ", + "で" =>"デ", + "と" =>"ト", + "ど" =>"ド", + "な" =>"ナ", + "に" =>"ニ", + "ぬ" =>"ヌ", + "ね" =>"ネ", + "の" =>"ノ", + "は" =>"ハ", + "ば" =>"バ", + "ぱ" =>"パ", + "ひ" =>"ヒ", + "び" =>"ビ", + "ぴ" =>"ピ", + "ふ" =>"フ", + "ぶ" =>"ブ", + "ぷ" =>"プ", + "へ" =>"ヘ", + "べ" =>"ベ", + "ぺ" =>"ペ", + "ほ" =>"ホ", + "ぼ" =>"ボ", + "ぽ" =>"ポ", + "ま" =>"マ", + "み" =>"ミ", + "む" =>"ム", + "め" =>"メ", + "も" =>"モ", + "ゃ" =>"ャ", + "や" =>"ヤ", + "ゅ" =>"ュ", + "ゆ" =>"ユ", + "ょ" =>"ョ", + "よ" =>"ヨ", + "ら" =>"ラ", + "り" =>"リ", + "る" =>"ル", + "れ" =>"レ", + "ろ" =>"ロ", + "わ" =>"ワ", + "を" =>"ヲ", + "ん" =>"ン", + "ゎ" =>"ヮ", + "ゐ" =>"ヰ", + "ゑ" =>"ヱ", + "ゕ" =>"ヵ", + "ゖ" =>"ヶ", + "ゔ" =>"ヴ", + "ゝ" =>"ヽ", + "ゞ" =>"ヾ", +]; +pub static ALLOWED_HW_KANA: &[&'static str] = &[ + "-", "-", "ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", + "ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ", "ダ", "チ", + "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", + "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ", "ム", "メ", "モ", "ャ", + "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ヲ", "ン", "-", "ヮ", "ヰ", "ヱ", "ヵ", + "ヶ", "ヴ", "ヽ", "ヾ", "・", "「", "」", "。", "、", "!", "\"", "#", "$", "%", "&", "'", "", ")", + "*", "+", ",", ".", "/", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", " ", "0", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", + "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", + "ゼ", "ソ", "ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", + "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", + "マ", "ミ", "ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ヲ", "ン", + "ヮ", "ヰ", "ヱ", "ヵ", "ヶ", "ヴ", "ヽ", "ヾ", +]; +pub static MULTI_BYTE_CHARS: phf::Map = phf_map![ + 'ガ' => 1, + 'ギ' => 1, + 'グ' => 1, + 'ゲ' => 1, + 'ゴ' => 1, + 'ザ' => 1, + 'ジ' => 1, + 'ズ' => 1, + 'ゼ' => 1, + 'ゾ' => 1, + 'ダ' => 1, + 'ヂ' => 1, + 'ヅ' => 1, + 'デ' => 1, + 'ド' => 1, + 'バ' => 1, + 'パ' => 1, + 'ビ' => 1, + 'ピ' => 1, + 'ブ' => 1, + 'プ' => 1, + 'ベ' => 1, + 'ペ' => 1, + 'ボ' => 1, + 'ポ' => 1, + 'ヮ' => 1, + 'ヰ' => 1, + 'ヱ' => 1, + 'ヵ' => 1, + 'ヶ' => 1, + 'ヴ' => 1, + 'ヽ' => 1, + 'ヾ' => 1, + 'が' => 1, + 'ぎ' => 1, + 'ぐ' => 1, + 'げ' => 1, + 'ご' => 1, + 'ざ' => 1, + 'じ' => 1, + 'ず' => 1, + 'ぜ' => 1, + 'ぞ' => 1, + 'だ' => 1, + 'ぢ' => 1, + 'づ' => 1, + 'で' => 1, + 'ど' => 1, + 'ば' => 1, + 'ぱ' => 1, + 'び' => 1, + 'ぴ' => 1, + 'ぶ' => 1, + 'ぷ' => 1, + 'べ' => 1, + 'ぺ' => 1, + 'ぼ' => 1, + 'ぽ' => 1, + 'ゎ' => 1, + 'ゐ' => 1, + 'ゑ' => 1, + 'ゕ' => 1, + 'ゖ' => 1, + 'ゔ' => 1, + 'ゝ' => 1, + 'ゞ' => 1 +]; +pub static HW_TO_FW_RANGE_MAP: phf::Map = phf_map![ + '0' => '0', + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + '7' => '7', + '8' => '8', + '9' => '9', + '-' => '-', + '/' => '/', + ';' => ';', ]; diff --git a/src/netmd/utils.rs b/src/netmd/utils.rs index 55d86a7..26cd0c2 100644 --- a/src/netmd/utils.rs +++ b/src/netmd/utils.rs @@ -2,12 +2,15 @@ use crate::netmd::mappings::{ALLOWED_HW_KANA, MAPPINGS_DE, MAPPINGS_HW, MAPPINGS use diacritics; use encoding_rs::SHIFT_JIS; use regex::Regex; -use std::{collections::hash_map::HashMap, error::Error, vec::IntoIter, time::Duration}; +use std::{error::Error, io::Write, time::Duration, vec::IntoIter}; use unicode_normalization::UnicodeNormalization; +use byteorder::{LittleEndian, WriteBytesExt}; extern crate kana; use kana::*; +use super::{interface::DiscFormat, mappings::{HW_TO_FW_RANGE_MAP, MULTI_BYTE_CHARS}}; + /// Sleep for a specified [Duration] on any platform pub async fn cross_sleep(duration: Duration) { #[cfg(not(target_family = "wasm"))] @@ -19,13 +22,13 @@ pub async fn cross_sleep(duration: Duration) { pub fn bcd_to_int(mut bcd: i32) -> i32 { let mut value = 0; - let mut nibble = 0; + let mut nybble = 0; while bcd != 0 { - let nibble_value = bcd & 0xf; + let nybble_value = bcd & 0xf; bcd >>= 4; - value += nibble_value * i32::pow(10, nibble); - nibble += 1; + value += nybble_value * i32::pow(10, nybble); + nybble += 1; } value @@ -46,25 +49,9 @@ pub fn int_to_bcd(mut value: i32) -> i32 { } pub fn half_width_to_full_width_range(range: &str) -> String { - let mappings: HashMap = HashMap::from([ - ('0', '0'), - ('1', '1'), - ('2', '2'), - ('3', '3'), - ('4', '4'), - ('5', '5'), - ('6', '6'), - ('7', '7'), - ('8', '8'), - ('9', '9'), - ('-', '-'), - ('/', '/'), - (';', ';'), - ]); - range .chars() - .map(|char| mappings.get(&char).unwrap()) + .map(|char| HW_TO_FW_RANGE_MAP.get(&char).unwrap()) .collect() } @@ -99,11 +86,15 @@ fn check(string: String) -> Option { None } -fn half_width_title_length(title: &str) { +pub fn half_width_title_length(title: &str) -> usize { + let multibyte_len = title.chars() + .map(|c| (*MULTI_BYTE_CHARS.get(&c).unwrap_or(&0) as usize)) + .reduce(|a, b| a + b).unwrap_or_default(); + title.len() + multibyte_len } -pub fn sanitize_half_width_title(title: &str) -> Vec { +pub fn sanitize_half_width_title(title: &str) -> String { let mut string_title = wide2ascii(title); string_title = nowidespace(&string_title); string_title = hira2kata(&string_title); @@ -118,17 +109,11 @@ pub fn sanitize_half_width_title(title: &str) -> Vec { }) .collect(); - let sjis_string = SHIFT_JIS.encode(&new_title).0; - - if validate_sjis(sjis_string.clone().into()) { - return agressive_sanitize_title(title).into(); - } - - sjis_string.into() + new_title } // TODO: This function is bad, probably should do the string sanitization in the frontend -pub fn sanitize_full_width_title(title: &str, just_remap: bool) -> Vec { +pub fn sanitize_full_width_title(title: &str) -> String { let new_title: String = title .chars() .map(|c| c.to_string()) @@ -155,14 +140,15 @@ pub fn sanitize_full_width_title(title: &str, just_remap: bool) -> Vec { }) .collect::(); - if just_remap { - return new_title.into(); - }; + new_title +} - let sjis_string = SHIFT_JIS.encode(&new_title).0; +/// Convert a UTF-8 string to Shift-JIS for use on the player +pub fn to_sjis(sjis_str: &str) -> Vec { + let sjis_string = SHIFT_JIS.encode(&sjis_str).0; if validate_sjis(sjis_string.clone().into()) { - return agressive_sanitize_title(title).into(); + return agressive_sanitize_title(sjis_str).into(); } sjis_string.into() @@ -179,6 +165,97 @@ pub fn agressive_sanitize_title(title: &str) -> String { .into() } +pub struct AeaOptions<'a> { + pub name: &'a str, + pub channels: u32, + pub sound_groups: u32, + pub group_start: u32, + pub encrypted: u32, + pub flags: &'a [u8], +} + +impl <'a> Default for AeaOptions<'a> { + fn default() -> Self { + Self { + name: "", + channels: 2, + sound_groups: 1, + group_start: 0, + encrypted: 0, + flags: &[0, 0, 0, 0, 0, 0, 0, 0] + } + } +} + +pub fn create_aea_header(options: AeaOptions) -> Vec { + let encoded_name = options.name.as_bytes(); + + let mut header: Vec = Vec::new(); + + header.write_u32::(2048).unwrap(); + header.write_all(encoded_name).unwrap(); + header.write_all(&vec![0; 256 - encoded_name.len()]).unwrap(); + header.write_u32::(options.sound_groups as u32).unwrap(); + header.write_all(&[options.channels as u8, 0]).unwrap(); + + // Write the flags + header.write_u32::(options.flags[0] as u32).unwrap(); + header.write_u32::(options.flags[1] as u32).unwrap(); + header.write_u32::(options.flags[2] as u32).unwrap(); + header.write_u32::(options.flags[3] as u32).unwrap(); + header.write_u32::(options.flags[4] as u32).unwrap(); + header.write_u32::(options.flags[5] as u32).unwrap(); + header.write_u32::(options.flags[6] as u32).unwrap(); + header.write_u32::(options.flags[7] as u32).unwrap(); + + header.write_u32::(0).unwrap(); + + header.write_u32::(options.encrypted as u32).unwrap(); + header.write_u32::(options.group_start as u32).unwrap(); + + // return the header + header +} + +pub fn create_wav_header(format: DiscFormat, bytes: u32) -> Vec { + let mut header: Vec = Vec::new(); + + let (joint_stereo, bytes_per_frame) = match format { + DiscFormat::LP4 => (192, 0), + DiscFormat::LP2 => (96, 1), + _ => unreachable!("Cannot create WAV header for disc type {:?}", format) + }; + + let bytes_per_second = (bytes_per_frame * 44100) / 512; + + header.write_all(r"RIFF".as_bytes()).unwrap(); + header.write_u32::(bytes + 60).unwrap(); + header.write_all(r"WAVEfmt".as_bytes()).unwrap(); + header.write_u32::(32).unwrap(); + header.write_u16::(0x270).unwrap(); // ATRAC3 + header.write_u16::(2).unwrap(); // Stereo + header.write_u32::(44100).unwrap(); + header.write_u32::(bytes_per_second).unwrap(); + header.write_u16::(bytes_per_frame as u16 * 2).unwrap(); + + header.write_all(&[0, 0]).unwrap(); + + header.write_u16::(14).unwrap(); + header.write_u16::(1).unwrap(); + header.write_u32::(bytes_per_frame).unwrap(); + header.write_u16::(joint_stereo).unwrap(); + header.write_u16::(joint_stereo).unwrap(); + + header.write_u16::(1).unwrap(); + header.write_u16::(0).unwrap(); + + header.write_all(r"data".as_bytes()).unwrap(); + + header.write_u32::(bytes).unwrap(); + + header +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct RawTime { pub hours: u64,