mirror of
https://github.com/G2-Games/minidisc-cli.git
synced 2025-04-19 11:42:53 -05:00
Implemented all functions in commands.rs
This commit is contained in:
parent
bf66c59799
commit
291e0ba049
8 changed files with 1126 additions and 981 deletions
|
@ -1,2 +1,6 @@
|
||||||
[target.'cfg(target_family = "wasm")']
|
[target.'cfg(target_family = "wasm")']
|
||||||
rustflags = ["--cfg=web_sys_unstable_apis"]
|
rustflags = ["--cfg=web_sys_unstable_apis"]
|
||||||
|
|
||||||
|
# Enable for testing WASM-only stuff
|
||||||
|
[build]
|
||||||
|
target = "wasm32-unknown-unknown"
|
||||||
|
|
|
@ -26,7 +26,7 @@ encoding_rs = "0.8.33"
|
||||||
nofmt = "1.0.0"
|
nofmt = "1.0.0"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
unicode-normalization = "0.1.22"
|
unicode-normalization = "0.1.22"
|
||||||
regex = "1.10.2"
|
regex = "1.10"
|
||||||
cross_usb = "0.3"
|
cross_usb = "0.3"
|
||||||
num-derive = "0.3.3"
|
num-derive = "0.3.3"
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
|
@ -39,9 +39,10 @@ tokio = { version = "1.36", features = ["sync"] }
|
||||||
g2-unicode-jp = "0.4.1"
|
g2-unicode-jp = "0.4.1"
|
||||||
thiserror = "1.0.57"
|
thiserror = "1.0.57"
|
||||||
phf = { version = "0.11.2", features = ["phf_macros", "macros"] }
|
phf = { version = "0.11.2", features = ["phf_macros", "macros"] }
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
|
||||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
[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]
|
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
|
||||||
dwarf-debug-info = true
|
dwarf-debug-info = true
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![cfg_attr(debug_assertions, allow(dead_code))]
|
#![cfg_attr(debug_assertions, allow(dead_code))]
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use nofmt;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -15,58 +14,54 @@ use super::utils::cross_sleep;
|
||||||
const BULK_WRITE_ENDPOINT: u8 = 0x02;
|
const BULK_WRITE_ENDPOINT: u8 = 0x02;
|
||||||
const BULK_READ_ENDPOINT: u8 = 0x81;
|
const BULK_READ_ENDPOINT: u8 = 0x81;
|
||||||
|
|
||||||
pub static DEVICE_IDS: Lazy<Box<[DeviceId]>> = Lazy::new(|| {
|
pub static DEVICE_IDS: &[DeviceId] = &[
|
||||||
nofmt::pls! {
|
DeviceId {vendor_id: 0x04dd, product_id: 0x7202, name: Some("Sharp IM-MT899H")},
|
||||||
Box::new([
|
DeviceId {vendor_id: 0x04dd, product_id: 0x9013, name: Some("Sharp IM-DR400")},
|
||||||
DeviceId {vendor_id: 0x04dd, product_id: 0x7202, name: Some(String::from("Sharp IM-MT899H"))},
|
DeviceId {vendor_id: 0x04dd, product_id: 0x9014, name: Some("Sharp IM-DR80")},
|
||||||
DeviceId {vendor_id: 0x04dd, product_id: 0x9013, name: Some(String::from("Sharp IM-DR400"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0034, name: Some("Sony PCLK-XX")},
|
||||||
DeviceId {vendor_id: 0x04dd, product_id: 0x9014, name: Some(String::from("Sharp IM-DR80"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0036, name: Some("Sony")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0034, name: Some(String::from("Sony PCLK-XX"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0075, name: Some("Sony MZ-N1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0036, name: Some(String::from("Sony"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x007c, name: Some("Sony")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0075, name: Some(String::from("Sony MZ-N1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0080, name: Some("Sony LAM-1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x007c, name: Some(String::from("Sony"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0081, name: Some("Sony MDS-JB980/MDS-NT1/MDS-JE780")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0080, name: Some(String::from("Sony LAM-1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0084, name: Some("Sony MZ-N505")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0081, name: Some(String::from("Sony MDS-JB980/MDS-NT1/MDS-JE780"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0085, name: Some("Sony MZ-S1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0084, name: Some(String::from("Sony MZ-N505"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0086, name: Some("Sony MZ-N707")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0085, name: Some(String::from("Sony MZ-S1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x008e, name: Some("Sony CMT-C7NT")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0086, name: Some(String::from("Sony MZ-N707"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0097, name: Some("Sony PCGA-MDN1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x008e, name: Some(String::from("Sony CMT-C7NT"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00ad, name: Some("Sony CMT-L7HD")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0097, name: Some(String::from("Sony PCGA-MDN1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00c6, name: Some("Sony MZ-N10")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00ad, name: Some(String::from("Sony CMT-L7HD"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00c7, name: Some("Sony MZ-N910")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c6, name: Some(String::from("Sony MZ-N10"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00c8, name: Some("Sony MZ-N710/NF810")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c7, name: Some(String::from("Sony MZ-N910"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00c9, name: Some("Sony MZ-N510/N610")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c8, name: Some(String::from("Sony MZ-N710/NF810"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00ca, name: Some("Sony MZ-NE410/NF520D")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00c9, name: Some(String::from("Sony MZ-N510/N610"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00e7, name: Some("Sony CMT-M333NT/M373NT")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00ca, name: Some(String::from("Sony MZ-NE410/NF520D"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x00eb, name: Some("Sony MZ-NE810/NE910")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00e7, name: Some(String::from("Sony CMT-M333NT/M373NT"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0101, name: Some("Sony LAM")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x00eb, name: Some(String::from("Sony MZ-NE810/NE910"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0113, name: Some("Aiwa AM-NX1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0101, name: Some(String::from("Sony LAM"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x013f, name: Some("Sony MDS-S500")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0113, name: Some(String::from("Aiwa AM-NX1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x014c, name: Some("Aiwa AM-NX9")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x013f, name: Some(String::from("Sony MDS-S500"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x017e, name: Some("Sony MZ-NH1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x014c, name: Some(String::from("Aiwa AM-NX9"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0180, name: Some("Sony MZ-NH3D")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x017e, name: Some(String::from("Sony MZ-NH1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0182, name: Some("Sony MZ-NH900")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0180, name: Some(String::from("Sony MZ-NH3D"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0184, name: Some("Sony MZ-NH700/NH800")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0182, name: Some(String::from("Sony MZ-NH900"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0186, name: Some("Sony MZ-NH600")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0184, name: Some(String::from("Sony MZ-NH700/NH800"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0187, name: Some("Sony MZ-NH600D")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0186, name: Some(String::from("Sony MZ-NH600"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0188, name: Some("Sony MZ-N920")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0187, name: Some(String::from("Sony MZ-NH600D"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x018a, name: Some("Sony LAM-3")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0188, name: Some(String::from("Sony MZ-N920"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x01e9, name: Some("Sony MZ-DH10P")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x018a, name: Some(String::from("Sony LAM-3"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0219, name: Some("Sony MZ-RH10")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x01e9, name: Some(String::from("Sony MZ-DH10P"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x021b, name: Some("Sony MZ-RH710/MZ-RH910")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0219, name: Some(String::from("Sony MZ-RH10"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x021d, name: Some("Sony CMT-AH10")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x021b, name: Some(String::from("Sony MZ-RH710/MZ-RH910"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x022c, name: Some("Sony CMT-AH10")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x021d, name: Some(String::from("Sony CMT-AH10"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x023c, name: Some("Sony DS-HMD1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x022c, name: Some(String::from("Sony CMT-AH10"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0286, name: Some("Sony MZ-RH1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x023c, name: Some(String::from("Sony DS-HMD1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x011a, name: Some("Sony CMT-SE7")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0286, name: Some(String::from("Sony MZ-RH1"))},
|
DeviceId {vendor_id: 0x054c, product_id: 0x0148, name: Some("Sony MDS-A1")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x011a, name: Some(String::from("Sony CMT-SE7"))},
|
DeviceId {vendor_id: 0x0b28, product_id: 0x1004, name: Some("Kenwood MDX-J9")},
|
||||||
DeviceId {vendor_id: 0x054c, product_id: 0x0148, name: Some(String::from("Sony MDS-A1"))},
|
DeviceId {vendor_id: 0x04da, product_id: 0x23b3, name: Some("Panasonic SJ-MR250")},
|
||||||
DeviceId {vendor_id: 0x0b28, product_id: 0x1004, name: Some(String::from("Kenwood MDX-J9"))},
|
DeviceId {vendor_id: 0x04da, product_id: 0x23b6, name: Some("Panasonic SJ-MR270")},
|
||||||
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_CROSSUSB: Lazy<Box<[cross_usb::DeviceFilter]>> = Lazy::new(|| {
|
pub static DEVICE_IDS_CROSSUSB: Lazy<Box<[cross_usb::DeviceFilter]>> = Lazy::new(|| {
|
||||||
DEVICE_IDS.iter().map(|d|{
|
DEVICE_IDS.iter().map(|d|{
|
||||||
|
@ -94,7 +89,7 @@ pub enum Status {
|
||||||
pub struct DeviceId {
|
pub struct DeviceId {
|
||||||
vendor_id: u16,
|
vendor_id: u16,
|
||||||
product_id: u16,
|
product_id: u16,
|
||||||
name: Option<String>,
|
name: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[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
|
/// Gets the device name, this is limited to the devices in the list
|
||||||
pub fn device_name(&self) -> &Option<String> {
|
pub fn device_name(&self) -> Option<&str> {
|
||||||
&self.model.name
|
self.model.name
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the vendor id
|
/// Gets the vendor id
|
||||||
|
@ -310,20 +305,22 @@ impl NetMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default chunksize should be 0x10000
|
// Default chunksize should be 0x10000
|
||||||
pub async fn read_bulk(
|
pub async fn read_bulk<F: Fn(usize, usize)>(
|
||||||
&mut self,
|
&mut self,
|
||||||
length: usize,
|
length: usize,
|
||||||
chunksize: usize,
|
chunksize: usize,
|
||||||
|
progress_callback: Option<F>,
|
||||||
) -> Result<Vec<u8>, NetMDError> {
|
) -> Result<Vec<u8>, 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)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_bulk_to_array(
|
pub async fn read_bulk_to_array<F: Fn(usize, usize)>(
|
||||||
&mut self,
|
&mut self,
|
||||||
length: usize,
|
length: usize,
|
||||||
chunksize: usize,
|
chunksize: usize,
|
||||||
|
progress_callback: Option<F>,
|
||||||
) -> Result<Vec<u8>, NetMDError> {
|
) -> Result<Vec<u8>, NetMDError> {
|
||||||
let mut final_result: Vec<u8> = Vec::new();
|
let mut final_result: Vec<u8> = Vec::new();
|
||||||
let mut done = 0;
|
let mut done = 0;
|
||||||
|
@ -341,6 +338,10 @@ impl NetMD {
|
||||||
Err(error) => return Err(NetMDError::UsbError(error)),
|
Err(error) => return Err(NetMDError::UsbError(error)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(cb) = &progress_callback {
|
||||||
|
cb(length, done)
|
||||||
|
}
|
||||||
|
|
||||||
final_result.extend_from_slice(&res);
|
final_result.extend_from_slice(&res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
#![cfg_attr(debug_assertions, allow(dead_code))]
|
#![cfg_attr(debug_assertions, allow(dead_code))]
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
use regex::Regex;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use cross_usb::Descriptor;
|
use cross_usb::Descriptor;
|
||||||
|
|
||||||
use crate::netmd::interface::DiscFlag;
|
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::interface::{Channels, Direction, DiscFormat, Encoding, InterfaceError, MDSession, MDTrack, NetMDInterface, TrackFlag};
|
||||||
use super::utils::cross_sleep;
|
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)]
|
#[derive(FromPrimitive, PartialEq, Eq)]
|
||||||
pub enum OperatingStatus {
|
pub enum OperatingStatus {
|
||||||
|
@ -49,17 +50,19 @@ pub struct Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Track {
|
impl Track {
|
||||||
pub fn chars_to_cells(len: usize) -> usize {
|
pub fn cells_for_title(&self) -> (usize, usize) {
|
||||||
f32::ceil(len as f32 / 7.0) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn cells_for_title(&mut self) {
|
|
||||||
let encoding_name_correction = match self.encoding {
|
let encoding_name_correction = match self.encoding {
|
||||||
Encoding::SP => 0,
|
Encoding::SP => 0,
|
||||||
_ => 1
|
_ => 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 {
|
impl Disc {
|
||||||
pub async fn track_count(&self) -> u16 {
|
pub fn track_count(&self) -> u16 {
|
||||||
self.groups.iter()
|
self.groups.iter()
|
||||||
.map(|g| g.tracks.len())
|
.map(|g| g.tracks.len())
|
||||||
.reduce(|acc, s| acc + s)
|
.reduce(|acc, s| acc + s)
|
||||||
.unwrap() as u16
|
.unwrap() as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn tracks(&self) -> Vec<Track> {
|
pub fn tracks(&self) -> Vec<Track> {
|
||||||
self.groups.iter()
|
self.groups.iter()
|
||||||
.flat_map(|g| g.tracks.clone())
|
.flat_map(|g| g.tracks.clone())
|
||||||
.collect()
|
.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<u16> = 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 {
|
pub struct NetMDContext {
|
||||||
|
@ -111,7 +238,6 @@ impl NetMDContext {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
/// Change to the next track (skip forward)
|
/// Change to the next track (skip forward)
|
||||||
pub async fn next_track(&mut self) -> Result<(), InterfaceError> {
|
pub async fn next_track(&mut self) -> Result<(), InterfaceError> {
|
||||||
self.interface.track_change(Direction::Next).await
|
self.interface.track_change(Direction::Next).await
|
||||||
|
@ -126,7 +252,6 @@ impl NetMDContext {
|
||||||
pub async fn restart_track(&mut self) -> Result<(), InterfaceError> {
|
pub async fn restart_track(&mut self) -> Result<(), InterfaceError> {
|
||||||
self.interface.track_change(Direction::Restart).await
|
self.interface.track_change(Direction::Restart).await
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
pub async fn device_status(&mut self) -> Result<DeviceStatus, Box<dyn Error>> {
|
pub async fn device_status(&mut self) -> Result<DeviceStatus, Box<dyn Error>> {
|
||||||
let status = self.interface.status().await?;
|
let status = self.interface.status().await?;
|
||||||
|
@ -158,46 +283,6 @@ impl NetMDContext {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn prepare_download(&mut self) -> Result<(), Box<dyn Error>> {
|
|
||||||
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<F>(
|
|
||||||
&mut self,
|
|
||||||
track: MDTrack,
|
|
||||||
progress_callback: F,
|
|
||||||
) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>>
|
|
||||||
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<Disc, Box<dyn Error>> {
|
pub async fn list_content(&mut self) -> Result<Disc, Box<dyn Error>> {
|
||||||
let flags = self.interface.disc_flags().await?;
|
let flags = self.interface.disc_flags().await?;
|
||||||
let title = self.interface.disc_title(false).await?;
|
let title = self.interface.disc_title(false).await?;
|
||||||
|
@ -265,4 +350,157 @@ impl NetMDContext {
|
||||||
|
|
||||||
Ok(disc)
|
Ok(disc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn rewrite_disc_groups(&mut self, disc: Disc) -> Result<(), Box<dyn Error>> {
|
||||||
|
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<dyn Error>> {
|
||||||
|
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<F: Fn(usize, usize)>(
|
||||||
|
&mut self,
|
||||||
|
track: u16,
|
||||||
|
progress_callback: Option<F>
|
||||||
|
) -> Result<(DiscFormat, Vec<u8>), Box<dyn Error>> {
|
||||||
|
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<dyn Error>> {
|
||||||
|
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<F>(
|
||||||
|
&mut self,
|
||||||
|
track: MDTrack,
|
||||||
|
progress_callback: F,
|
||||||
|
) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>>
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,5 +73,7 @@ pub fn new_thread_encryptor(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rx
|
rx
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::error::Error;
|
||||||
use tokio::sync::mpsc::UnboundedReceiver;
|
use tokio::sync::mpsc::UnboundedReceiver;
|
||||||
|
|
||||||
use super::base::NetMD;
|
use super::base::NetMD;
|
||||||
use super::utils::cross_sleep;
|
use super::utils::{cross_sleep, to_sjis};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum Action {
|
enum Action {
|
||||||
|
@ -34,7 +34,7 @@ pub enum Direction {
|
||||||
Restart = 0x0001,
|
Restart = 0x0001,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, FromPrimitive)]
|
#[derive(Debug, Clone, Copy, FromPrimitive, PartialEq, Eq)]
|
||||||
pub enum DiscFormat {
|
pub enum DiscFormat {
|
||||||
LP4 = 0,
|
LP4 = 0,
|
||||||
LP2 = 2,
|
LP2 = 2,
|
||||||
|
@ -846,7 +846,7 @@ impl NetMDInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the disc title as it is stored
|
/// Gets the disc title as it is stored
|
||||||
async fn raw_disc_title(&mut self, wchar: bool) -> Result<String, InterfaceError> {
|
pub async fn raw_disc_title(&mut self, wchar: bool) -> Result<String, InterfaceError> {
|
||||||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
|
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
|
||||||
.await?;
|
.await?;
|
||||||
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenRead)
|
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenRead)
|
||||||
|
@ -1098,11 +1098,11 @@ impl NetMDInterface {
|
||||||
|
|
||||||
let wchar_value = match wchar {
|
let wchar_value = match wchar {
|
||||||
true => {
|
true => {
|
||||||
new_title = sanitize_full_width_title(title, false);
|
new_title = to_sjis(&sanitize_full_width_title(title));
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
new_title = sanitize_half_width_title(title);
|
new_title = to_sjis(&sanitize_half_width_title(title));
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1156,11 +1156,11 @@ impl NetMDInterface {
|
||||||
let new_title: Vec<u8>;
|
let new_title: Vec<u8>;
|
||||||
let (wchar_value, descriptor) = match wchar {
|
let (wchar_value, descriptor) = match wchar {
|
||||||
true => {
|
true => {
|
||||||
new_title = sanitize_full_width_title(title, false);
|
new_title = to_sjis(&sanitize_full_width_title(title));
|
||||||
(3, Descriptor::AudioUTOC4TD)
|
(3, Descriptor::AudioUTOC4TD)
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
new_title = sanitize_half_width_title(title);
|
new_title = to_sjis(&sanitize_half_width_title(title));
|
||||||
(2, Descriptor::AudioUTOC1TD)
|
(2, Descriptor::AudioUTOC1TD)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1418,9 +1418,10 @@ impl NetMDInterface {
|
||||||
/// Gets the bytes of a track
|
/// Gets the bytes of a track
|
||||||
///
|
///
|
||||||
/// This can only be executed on an MZ-RH1 / M200
|
/// This can only be executed on an MZ-RH1 / M200
|
||||||
pub async fn save_track_to_array(
|
pub async fn save_track_to_array<F: Fn(usize, usize)>(
|
||||||
&mut self,
|
&mut self,
|
||||||
track: u16,
|
track: u16,
|
||||||
|
progress_callback: Option<F>
|
||||||
) -> Result<(DiscFormat, u16, Vec<u8>), InterfaceError> {
|
) -> Result<(DiscFormat, u16, Vec<u8>), InterfaceError> {
|
||||||
let mut query = format_query(
|
let mut query = format_query(
|
||||||
"1800 080046 f003010330 ff00 1001 %w".to_string(),
|
"1800 080046 f003010330 ff00 1001 %w".to_string(),
|
||||||
|
@ -1438,14 +1439,14 @@ impl NetMDInterface {
|
||||||
let codec = res[1].to_i64().unwrap() as u8;
|
let codec = res[1].to_i64().unwrap() as u8;
|
||||||
let length = res[2].to_i64().unwrap() as usize;
|
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(
|
scan_query(
|
||||||
self.read_reply(false).await?,
|
self.read_reply(false).await?,
|
||||||
"1800 080046 f003010330 0000 1001 %?%? %?%?".to_string(),
|
"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 {
|
let format: DiscFormat = match codec & 0x06 {
|
||||||
0 => DiscFormat::LP4,
|
0 => DiscFormat::LP4,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use phf::phf_map;
|
use phf::phf_map;
|
||||||
|
|
||||||
pub static MAPPINGS_JP: phf::Map<&'static str, &'static str> = phf_map!{
|
pub static MAPPINGS_JP: phf::Map<&'static str, &'static str> = phf_map![
|
||||||
"!" =>"!",
|
"!" =>"!",
|
||||||
"\"" =>""",
|
"\"" =>""",
|
||||||
"#" =>"#",
|
"#" =>"#",
|
||||||
|
@ -190,8 +190,8 @@ pub static MAPPINGS_JP: phf::Map<&'static str, &'static str> = phf_map!{
|
||||||
"」" =>"」",
|
"」" =>"」",
|
||||||
"。" =>"。",
|
"。" =>"。",
|
||||||
"、" =>"、"
|
"、" =>"、"
|
||||||
};
|
];
|
||||||
pub static MAPPINGS_RU: phf::Map<&'static str, &'static str> = phf_map!{
|
pub static MAPPINGS_RU: phf::Map<&'static str, &'static str> = phf_map![
|
||||||
"а" =>"a",
|
"а" =>"a",
|
||||||
"б" =>"b",
|
"б" =>"b",
|
||||||
"в" =>"v",
|
"в" =>"v",
|
||||||
|
@ -258,8 +258,8 @@ pub static MAPPINGS_RU: phf::Map<&'static str, &'static str> = phf_map!{
|
||||||
"Э" =>"E",
|
"Э" =>"E",
|
||||||
"Ю" =>"Iu",
|
"Ю" =>"Iu",
|
||||||
"Я" =>"Ia"
|
"Я" =>"Ia"
|
||||||
};
|
];
|
||||||
pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map!{
|
pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map![
|
||||||
"Ä" => "Ae",
|
"Ä" => "Ae",
|
||||||
"ä" => "ae",
|
"ä" => "ae",
|
||||||
"Ö" => "Oe",
|
"Ö" => "Oe",
|
||||||
|
@ -267,8 +267,8 @@ pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map!{
|
||||||
"Ü" => "Ue",
|
"Ü" => "Ue",
|
||||||
"ü" => "ue",
|
"ü" => "ue",
|
||||||
"ß" => "ss"
|
"ß" => "ss"
|
||||||
};
|
];
|
||||||
pub static MAPPINGS_HW: phf::Map<&'static str, &'static str> = phf_map!{
|
pub static MAPPINGS_HW: phf::Map<&'static str, &'static str> = phf_map![
|
||||||
"-" =>"-",
|
"-" =>"-",
|
||||||
"ー" =>"-",
|
"ー" =>"-",
|
||||||
"ァ" =>"ァ",
|
"ァ" =>"ァ",
|
||||||
|
@ -547,284 +547,105 @@ pub static MAPPINGS_HW: phf::Map<&'static str, &'static str> = phf_map!{
|
||||||
"ゔ" =>"ヴ",
|
"ゔ" =>"ヴ",
|
||||||
"ゝ" =>"ヽ",
|
"ゝ" =>"ヽ",
|
||||||
"ゞ" =>"ヾ",
|
"ゞ" =>"ヾ",
|
||||||
};
|
];
|
||||||
pub static ALLOWED_HW_KANA: &[&'static str] = &[
|
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<char, u8> = 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<char, char> = phf_map![
|
||||||
"ヵ",
|
'0' => '0',
|
||||||
"ヶ",
|
'1' => '1',
|
||||||
"ヴ",
|
'2' => '2',
|
||||||
"ヽ",
|
'3' => '3',
|
||||||
"ヾ",
|
'4' => '4',
|
||||||
"・",
|
'5' => '5',
|
||||||
"「",
|
'6' => '6',
|
||||||
"」",
|
'7' => '7',
|
||||||
"。",
|
'8' => '8',
|
||||||
"、",
|
'9' => '9',
|
||||||
"!",
|
'-' => '-',
|
||||||
"\"",
|
'/' => '/',
|
||||||
"#",
|
';' => ';',
|
||||||
"$",
|
|
||||||
"%",
|
|
||||||
"&",
|
|
||||||
"'",
|
|
||||||
"",
|
|
||||||
")",
|
|
||||||
"*",
|
|
||||||
"+",
|
|
||||||
",",
|
|
||||||
".",
|
|
||||||
"/",
|
|
||||||
":",
|
|
||||||
";",
|
|
||||||
"<",
|
|
||||||
"=",
|
|
||||||
">",
|
|
||||||
"?",
|
|
||||||
"@",
|
|
||||||
"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",
|
|
||||||
"ァ",
|
|
||||||
"ア",
|
|
||||||
"ィ",
|
|
||||||
"イ",
|
|
||||||
"ゥ",
|
|
||||||
"ウ",
|
|
||||||
"ェ",
|
|
||||||
"エ",
|
|
||||||
"ォ",
|
|
||||||
"オ",
|
|
||||||
"カ",
|
|
||||||
"ガ",
|
|
||||||
"キ",
|
|
||||||
"ギ",
|
|
||||||
"ク",
|
|
||||||
"グ",
|
|
||||||
"ケ",
|
|
||||||
"ゲ",
|
|
||||||
"コ",
|
|
||||||
"ゴ",
|
|
||||||
"サ",
|
|
||||||
"ザ",
|
|
||||||
"シ",
|
|
||||||
"ジ",
|
|
||||||
"ス",
|
|
||||||
"ズ",
|
|
||||||
"セ",
|
|
||||||
"ゼ",
|
|
||||||
"ソ",
|
|
||||||
"ゾ",
|
|
||||||
"タ",
|
|
||||||
"ダ",
|
|
||||||
"チ",
|
|
||||||
"ヂ",
|
|
||||||
"ッ",
|
|
||||||
"ツ",
|
|
||||||
"ヅ",
|
|
||||||
"テ",
|
|
||||||
"デ",
|
|
||||||
"ト",
|
|
||||||
"ド",
|
|
||||||
"ナ",
|
|
||||||
"ニ",
|
|
||||||
"ヌ",
|
|
||||||
"ネ",
|
|
||||||
"ノ",
|
|
||||||
"ハ",
|
|
||||||
"バ",
|
|
||||||
"パ",
|
|
||||||
"ヒ",
|
|
||||||
"ビ",
|
|
||||||
"ピ",
|
|
||||||
"フ",
|
|
||||||
"ブ",
|
|
||||||
"プ",
|
|
||||||
"ヘ",
|
|
||||||
"ベ",
|
|
||||||
"ペ",
|
|
||||||
"ホ",
|
|
||||||
"ボ",
|
|
||||||
"ポ",
|
|
||||||
"マ",
|
|
||||||
"ミ",
|
|
||||||
"ム",
|
|
||||||
"メ",
|
|
||||||
"モ",
|
|
||||||
"ャ",
|
|
||||||
"ヤ",
|
|
||||||
"ュ",
|
|
||||||
"ユ",
|
|
||||||
"ョ",
|
|
||||||
"ヨ",
|
|
||||||
"ラ",
|
|
||||||
"リ",
|
|
||||||
"ル",
|
|
||||||
"レ",
|
|
||||||
"ロ",
|
|
||||||
"ワ",
|
|
||||||
"ヲ",
|
|
||||||
"ン",
|
|
||||||
"ヮ",
|
|
||||||
"ヰ",
|
|
||||||
"ヱ",
|
|
||||||
"ヵ",
|
|
||||||
"ヶ",
|
|
||||||
"ヴ",
|
|
||||||
"ヽ",
|
|
||||||
"ヾ",
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -2,12 +2,15 @@ use crate::netmd::mappings::{ALLOWED_HW_KANA, MAPPINGS_DE, MAPPINGS_HW, MAPPINGS
|
||||||
use diacritics;
|
use diacritics;
|
||||||
use encoding_rs::SHIFT_JIS;
|
use encoding_rs::SHIFT_JIS;
|
||||||
use regex::Regex;
|
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 unicode_normalization::UnicodeNormalization;
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
|
||||||
extern crate kana;
|
extern crate kana;
|
||||||
use kana::*;
|
use kana::*;
|
||||||
|
|
||||||
|
use super::{interface::DiscFormat, mappings::{HW_TO_FW_RANGE_MAP, MULTI_BYTE_CHARS}};
|
||||||
|
|
||||||
/// Sleep for a specified [Duration] on any platform
|
/// Sleep for a specified [Duration] on any platform
|
||||||
pub async fn cross_sleep(duration: Duration) {
|
pub async fn cross_sleep(duration: Duration) {
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[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 {
|
pub fn bcd_to_int(mut bcd: i32) -> i32 {
|
||||||
let mut value = 0;
|
let mut value = 0;
|
||||||
let mut nibble = 0;
|
let mut nybble = 0;
|
||||||
|
|
||||||
while bcd != 0 {
|
while bcd != 0 {
|
||||||
let nibble_value = bcd & 0xf;
|
let nybble_value = bcd & 0xf;
|
||||||
bcd >>= 4;
|
bcd >>= 4;
|
||||||
value += nibble_value * i32::pow(10, nibble);
|
value += nybble_value * i32::pow(10, nybble);
|
||||||
nibble += 1;
|
nybble += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
value
|
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 {
|
pub fn half_width_to_full_width_range(range: &str) -> String {
|
||||||
let mappings: HashMap<char, char> = HashMap::from([
|
|
||||||
('0', '0'),
|
|
||||||
('1', '1'),
|
|
||||||
('2', '2'),
|
|
||||||
('3', '3'),
|
|
||||||
('4', '4'),
|
|
||||||
('5', '5'),
|
|
||||||
('6', '6'),
|
|
||||||
('7', '7'),
|
|
||||||
('8', '8'),
|
|
||||||
('9', '9'),
|
|
||||||
('-', '-'),
|
|
||||||
('/', '/'),
|
|
||||||
(';', ';'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
range
|
range
|
||||||
.chars()
|
.chars()
|
||||||
.map(|char| mappings.get(&char).unwrap())
|
.map(|char| HW_TO_FW_RANGE_MAP.get(&char).unwrap())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,11 +86,15 @@ fn check(string: String) -> Option<String> {
|
||||||
None
|
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<u8> {
|
pub fn sanitize_half_width_title(title: &str) -> String {
|
||||||
let mut string_title = wide2ascii(title);
|
let mut string_title = wide2ascii(title);
|
||||||
string_title = nowidespace(&string_title);
|
string_title = nowidespace(&string_title);
|
||||||
string_title = hira2kata(&string_title);
|
string_title = hira2kata(&string_title);
|
||||||
|
@ -118,17 +109,11 @@ pub fn sanitize_half_width_title(title: &str) -> Vec<u8> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let sjis_string = SHIFT_JIS.encode(&new_title).0;
|
new_title
|
||||||
|
|
||||||
if validate_sjis(sjis_string.clone().into()) {
|
|
||||||
return agressive_sanitize_title(title).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
sjis_string.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This function is bad, probably should do the string sanitization in the frontend
|
// 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<u8> {
|
pub fn sanitize_full_width_title(title: &str) -> String {
|
||||||
let new_title: String = title
|
let new_title: String = title
|
||||||
.chars()
|
.chars()
|
||||||
.map(|c| c.to_string())
|
.map(|c| c.to_string())
|
||||||
|
@ -155,14 +140,15 @@ pub fn sanitize_full_width_title(title: &str, just_remap: bool) -> Vec<u8> {
|
||||||
})
|
})
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
if just_remap {
|
new_title
|
||||||
return new_title.into();
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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<u8> {
|
||||||
|
let sjis_string = SHIFT_JIS.encode(&sjis_str).0;
|
||||||
|
|
||||||
if validate_sjis(sjis_string.clone().into()) {
|
if validate_sjis(sjis_string.clone().into()) {
|
||||||
return agressive_sanitize_title(title).into();
|
return agressive_sanitize_title(sjis_str).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
sjis_string.into()
|
sjis_string.into()
|
||||||
|
@ -179,6 +165,97 @@ pub fn agressive_sanitize_title(title: &str) -> String {
|
||||||
.into()
|
.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<u8> {
|
||||||
|
let encoded_name = options.name.as_bytes();
|
||||||
|
|
||||||
|
let mut header: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
header.write_u32::<LittleEndian>(2048).unwrap();
|
||||||
|
header.write_all(encoded_name).unwrap();
|
||||||
|
header.write_all(&vec![0; 256 - encoded_name.len()]).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.sound_groups as u32).unwrap();
|
||||||
|
header.write_all(&[options.channels as u8, 0]).unwrap();
|
||||||
|
|
||||||
|
// Write the flags
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[0] as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[1] as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[2] as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[3] as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[4] as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[5] as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[6] as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.flags[7] as u32).unwrap();
|
||||||
|
|
||||||
|
header.write_u32::<LittleEndian>(0).unwrap();
|
||||||
|
|
||||||
|
header.write_u32::<LittleEndian>(options.encrypted as u32).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(options.group_start as u32).unwrap();
|
||||||
|
|
||||||
|
// return the header
|
||||||
|
header
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_wav_header(format: DiscFormat, bytes: u32) -> Vec<u8> {
|
||||||
|
let mut header: Vec<u8> = 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::<LittleEndian>(bytes + 60).unwrap();
|
||||||
|
header.write_all(r"WAVEfmt".as_bytes()).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(32).unwrap();
|
||||||
|
header.write_u16::<LittleEndian>(0x270).unwrap(); // ATRAC3
|
||||||
|
header.write_u16::<LittleEndian>(2).unwrap(); // Stereo
|
||||||
|
header.write_u32::<LittleEndian>(44100).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(bytes_per_second).unwrap();
|
||||||
|
header.write_u16::<LittleEndian>(bytes_per_frame as u16 * 2).unwrap();
|
||||||
|
|
||||||
|
header.write_all(&[0, 0]).unwrap();
|
||||||
|
|
||||||
|
header.write_u16::<LittleEndian>(14).unwrap();
|
||||||
|
header.write_u16::<LittleEndian>(1).unwrap();
|
||||||
|
header.write_u32::<LittleEndian>(bytes_per_frame).unwrap();
|
||||||
|
header.write_u16::<LittleEndian>(joint_stereo).unwrap();
|
||||||
|
header.write_u16::<LittleEndian>(joint_stereo).unwrap();
|
||||||
|
|
||||||
|
header.write_u16::<LittleEndian>(1).unwrap();
|
||||||
|
header.write_u16::<LittleEndian>(0).unwrap();
|
||||||
|
|
||||||
|
header.write_all(r"data".as_bytes()).unwrap();
|
||||||
|
|
||||||
|
header.write_u32::<LittleEndian>(bytes).unwrap();
|
||||||
|
|
||||||
|
header
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct RawTime {
|
pub struct RawTime {
|
||||||
pub hours: u64,
|
pub hours: u64,
|
||||||
|
|
Loading…
Reference in a new issue