Cleaned up old code, added new functions to commands.rs

This commit is contained in:
G2-Games 2024-05-28 18:03:35 -05:00
parent 4151a4f5c0
commit bf66c59799
6 changed files with 1125 additions and 652 deletions

View file

@ -27,7 +27,6 @@ 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.2"
lazy_static = "1.4.0"
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,6 +38,7 @@ ecb = "0.1"
tokio = { version = "1.36", features = ["sync"] } 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"] }
[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"] }

View file

@ -5,7 +5,10 @@ use std::error::Error;
use std::time::Duration; use std::time::Duration;
use cross_usb::Descriptor; use cross_usb::Descriptor;
use super::interface::{MDSession, MDTrack, NetMDInterface, Direction, InterfaceError}; use crate::netmd::interface::DiscFlag;
use crate::netmd::utils::RawTime;
use super::interface::{Channels, Encoding, InterfaceError, MDSession, MDTrack, NetMDInterface, TrackFlag};
use super::utils::cross_sleep; use super::utils::cross_sleep;
#[derive(FromPrimitive, PartialEq, Eq)] #[derive(FromPrimitive, PartialEq, Eq)]
@ -34,6 +37,66 @@ pub struct DeviceStatus {
pub time: Time, pub time: Time,
} }
#[derive(Clone)]
pub struct Track {
index: u16,
title: String,
full_width_title: String,
duration: RawTime,
channel: Channels,
encoding: Encoding,
protected: TrackFlag,
}
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) {
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);
}
}
pub struct Group {
index: u16,
title: Option<String>,
full_width_title: Option<String>,
tracks: Vec<Track>,
}
pub struct Disc {
title: String,
full_width_title: String,
writeable: bool,
write_protected: bool,
used: u64,
left: u64,
total: u64,
track_count: u16,
groups: Vec<Group>,
}
impl Disc {
pub async 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<Track> {
self.groups.iter()
.flat_map(|g| g.tracks.clone())
.collect()
}
}
pub struct NetMDContext { pub struct NetMDContext {
interface: NetMDInterface, interface: NetMDInterface,
} }
@ -48,6 +111,7 @@ 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
@ -62,6 +126,7 @@ 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?;
@ -132,4 +197,72 @@ impl NetMDContext {
Ok(result) Ok(result)
} }
pub async fn list_content(&mut self) -> Result<Disc, Box<dyn Error>> {
let flags = self.interface.disc_flags().await?;
let title = self.interface.disc_title(false).await?;
let full_width_title = self.interface.disc_title(true).await?;
let disc_capacity: [RawTime; 3] = self.interface.disc_capacity().await?;
let track_count = self.interface.track_count().await?;
let mut frames_used = disc_capacity[0].as_frames();
let mut frames_total = disc_capacity[1].as_frames();
let mut frames_left = disc_capacity[2].as_frames();
// Some devices report the time remaining of the currently selected recording mode. (Sharps)
while frames_total > 512 * 60 * 82 {
frames_used /= 2;
frames_total /= 2;
frames_left /= 2;
}
let track_group_list = self.interface.track_group_list().await?;
let mut groups = vec![];
for (index, group) in track_group_list.iter().enumerate() {
let mut tracks = vec![];
for track in &group.2 {
let (encoding, channel) = self.interface.track_encoding(*track).await?;
let duration = self.interface.track_length(*track).await?;
let flags = self.interface.track_flags(*track).await?;
let title = self.interface.track_title(*track, false).await?;
let full_width_title = self.interface.track_title(*track, true).await?;
tracks.push(
Track {
index: *track,
title,
full_width_title,
duration,
channel,
encoding,
protected: TrackFlag::from_u8(flags).unwrap(),
}
)
}
groups.push(
Group {
index: index as u16,
title: group.0.clone(),
full_width_title: group.1.clone(),
tracks
}
)
}
let disc = Disc {
title,
full_width_title,
writeable: (flags & DiscFlag::Writable as u8) != 0,
write_protected: (flags & DiscFlag::WriteProtected as u8) != 0,
used: frames_used,
left: frames_left,
total: frames_total,
track_count,
groups
};
Ok(disc)
}
} }

View file

@ -2,8 +2,9 @@
use crate::netmd::base; use crate::netmd::base;
use crate::netmd::query_utils::{format_query, scan_query, QueryValue}; use crate::netmd::query_utils::{format_query, scan_query, QueryValue};
use crate::netmd::utils::{ use crate::netmd::utils::{
half_width_to_full_width_range, length_after_encoding_to_jis, sanitize_full_width_title, half_width_to_full_width_range, length_after_encoding_to_sjis, sanitize_full_width_title,
sanitize_half_width_title, time_to_duration, sanitize_half_width_title,
RawTime,
}; };
use cbc::cipher::block_padding::NoPadding; use cbc::cipher::block_padding::NoPadding;
use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit}; use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit};
@ -16,8 +17,6 @@ use thiserror::Error;
use std::error::Error; use std::error::Error;
use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedReceiver;
use lazy_static::lazy_static;
use super::base::NetMD; use super::base::NetMD;
use super::utils::cross_sleep; use super::utils::cross_sleep;
@ -52,7 +51,7 @@ pub enum WireFormat {
} }
impl WireFormat { impl WireFormat {
fn frame_size(&self) -> u16 { const fn frame_size(&self) -> u16 {
match self { match self {
WireFormat::Pcm => 2048, WireFormat::Pcm => 2048,
WireFormat::L105kbps => 192, WireFormat::L105kbps => 192,
@ -60,31 +59,70 @@ impl WireFormat {
WireFormat::LP4 => 96, WireFormat::LP4 => 96,
} }
} }
const fn disc_for_wire(&self) -> DiscFormat {
match self {
WireFormat::Pcm => DiscFormat::SPStereo,
WireFormat::L105kbps => DiscFormat::LP2,
WireFormat::LP2 => DiscFormat::LP2,
WireFormat::LP4 => DiscFormat::LP4,
}
}
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub enum Encoding { pub enum Encoding {
SP = 0x90, SP = 0x90,
LP2 = 0x92, LP2 = 0x92,
LP4 = 0x93, LP4 = 0x93,
} }
enum Channels { impl ToString for Encoding {
fn to_string(&self) -> String {
match self {
Encoding::SP => String::from("sp"),
Encoding::LP2 => String::from("lp2"),
Encoding::LP4 => String::from("lp4"),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Channels {
Mono = 0x01, Mono = 0x01,
Stereo = 0x00, Stereo = 0x00,
} }
impl ToString for Channels {
fn to_string(&self) -> String {
match self {
Channels::Mono => String::from("mono"),
Channels::Stereo => String::from("stereo"),
}
}
}
enum ChannelCount { enum ChannelCount {
Mono = 1, Mono = 1,
Stereo = 2, Stereo = 2,
} }
enum TrackFlag { #[derive(Debug, Clone, Copy, FromPrimitive)]
pub enum TrackFlag {
Protected = 0x03, Protected = 0x03,
Unprotected = 0x00, Unprotected = 0x00,
} }
enum DiscFlag { impl ToString for TrackFlag {
fn to_string(&self) -> String {
match self {
TrackFlag::Protected => String::from("protected"),
TrackFlag::Unprotected => String::from("unprotected"),
}
}
}
pub enum DiscFlag {
Writable = 0x10, Writable = 0x10,
WriteProtected = 0x40, WriteProtected = 0x40,
} }
@ -161,15 +199,6 @@ enum NetmdStatus {
Interim = 0x0f, Interim = 0x0f,
} }
lazy_static! {
static ref FRAME_SIZE: HashMap<WireFormat, usize> = HashMap::from([
(WireFormat::Pcm, 2048),
(WireFormat::LP2, 192),
(WireFormat::L105kbps, 152),
(WireFormat::LP4, 96),
]);
}
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[error("invalid status code")] #[error("invalid status code")]
pub struct StatusError; pub struct StatusError;
@ -1065,7 +1094,7 @@ impl NetMDInterface {
} }
let new_title: Vec<u8>; let new_title: Vec<u8>;
let old_len = length_after_encoding_to_jis(&current_title); let old_len = length_after_encoding_to_sjis(&current_title);
let wchar_value = match wchar { let wchar_value = match wchar {
true => { true => {
@ -1143,7 +1172,7 @@ impl NetMDInterface {
if title == current_title { if title == current_title {
return Ok(()); return Ok(());
} }
length_after_encoding_to_jis(&current_title) as u16 length_after_encoding_to_sjis(&current_title) as u16
} }
Err(error) if error.to_string() == "Rejected" => 0, Err(error) if error.to_string() == "Rejected" => 0,
Err(error) => return Err(error), Err(error) => return Err(error),
@ -1232,12 +1261,12 @@ impl NetMDInterface {
Ok(res[0].to_vec().unwrap()) Ok(res[0].to_vec().unwrap())
} }
/// Gets the length of tracks as a [std::time::Duration] from a set /// Gets the length of tracks as a raw duration from a set
pub async fn track_lengths( pub async fn track_lengths(
&mut self, &mut self,
tracks: Vec<u16>, tracks: Vec<u16>,
) -> Result<Vec<std::time::Duration>, InterfaceError> { ) -> Result<Vec<RawTime>, InterfaceError> {
let mut times: Vec<std::time::Duration> = vec![]; let mut times = vec![];
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
.await?; .await?;
@ -1269,8 +1298,12 @@ impl NetMDInterface {
.map(|v| v.to_i64().unwrap() as u64) .map(|v| v.to_i64().unwrap() as u64)
.collect(); .collect();
let length = time_to_duration(&times_num); times.push(RawTime {
times.push(length); hours: times_num[0],
minutes: times_num[1],
seconds: times_num[2],
frames: times_num[3],
});
} }
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close) self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close)
@ -1279,20 +1312,20 @@ impl NetMDInterface {
Ok(times) Ok(times)
} }
/// Gets the length of a track as a [std::time::Duration] /// Gets the length of a track as a raw duration
pub async fn track_length( pub async fn track_length(
&mut self, &mut self,
track: u16, track: u16,
) -> Result<std::time::Duration, InterfaceError> { ) -> Result<RawTime, InterfaceError> {
Ok(self.track_lengths([track].into()).await?[0]) Ok(self.track_lengths([track].into()).await?[0])
} }
/// Gets the encoding of a track (SP, LP2, LP4) /// Gets the encoding of a track (SP, LP2, LP4)
pub async fn track_encoding(&mut self, track_number: u16) -> Result<Encoding, InterfaceError> { pub async fn track_encoding(&mut self, track_number: u16) -> Result<(Encoding, Channels), InterfaceError> {
let raw_value = self.raw_track_info(track_number, 0x3080, 0x0700).await?; let raw_value = self.raw_track_info(track_number, 0x3080, 0x0700).await?;
let result = scan_query(raw_value, "07 0004 0110 %b %b".to_string())?; let result = scan_query(raw_value, "07 0004 0110 %b %b".to_string())?;
let final_encoding = match result[0].to_i64() { let encoding = match result[0].to_i64() {
Ok(0x90) => Encoding::SP, Ok(0x90) => Encoding::SP,
Ok(0x92) => Encoding::LP2, Ok(0x92) => Encoding::LP2,
Ok(0x93) => Encoding::LP4, Ok(0x93) => Encoding::LP4,
@ -1300,7 +1333,14 @@ impl NetMDInterface {
Err(_) => unreachable!(), Err(_) => unreachable!(),
}; };
Ok(final_encoding) let channels = match result[0].to_i64() {
Ok(0x01) => Channels::Stereo,
Ok(0x00) => Channels::Mono,
Ok(e) => return Err(InterfaceError::InvalidEncoding(e as u8)),
Err(_) => unreachable!(),
};
Ok((encoding, channels))
} }
/// Gets a track's flags /// Gets a track's flags
@ -1323,36 +1363,34 @@ impl NetMDInterface {
} }
/// Gets the disc capacity as a [std::time::Duration] /// Gets the disc capacity as a [std::time::Duration]
pub async fn disc_capacity(&mut self) -> Result<[std::time::Duration; 3], InterfaceError> { pub async fn disc_capacity(&mut self) -> Result<[RawTime; 3], InterfaceError> {
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead) self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead)
.await?; .await?;
let mut query = format_query("1806 02101000 3080 0300 ff00 00000000".to_string(), vec![])?; let mut query = format_query("1806 02101000 3080 0300 ff00 00000000".to_string(), vec![])?;
let reply = self.send_query(&mut query, false, false).await?; let reply = self.send_query(&mut query, false, false).await?;
let mut result: [std::time::Duration; 3] = [std::time::Duration::from_secs(0); 3];
// 8003 changed to %?03 - Panasonic returns 0803 instead. This byte's meaning is unknown // 8003 changed to %?03 - Panasonic returns 0803 instead. This byte's meaning is unknown
let res = scan_query( let res = scan_query(
reply, reply,
"1806 02101000 3080 0300 1000 001d0000 001b %?03 0017 8000 0005 %W %B %B %B 0005 %W %B %B %B 0005 %W %B %B %B".to_string() "1806 02101000 3080 0300 1000 001d0000 001b %?03 0017 8000 0005 %W %B %B %B 0005 %W %B %B %B 0005 %W %B %B %B".to_string()
)?; //25^ )?;
let res_num: Vec<u64> = res
.into_iter()
.map(|v| v.to_i64().unwrap() as u64)
.collect();
// Create 3 values, `Frames Used`, `Frames Total`, and `Frames Left` let res_num: Vec<RawTime> = res
for i in 0..3 { .windows(4)
let tmp = &res_num[(4 * i)..=(4 * i) + 3]; .step_by(4)
let time_micros = .map(|t| RawTime {
(tmp[0] * 3600000000) + (tmp[1] * 60000000) + (tmp[2] * 1000000) + (tmp[3] * 11600); hours: t[0].to_i64().unwrap() as u64,
result[i] = std::time::Duration::from_micros(time_micros); minutes: t[1].to_i64().unwrap() as u64,
} seconds: t[2].to_i64().unwrap() as u64,
frames: t[3].to_i64().unwrap() as u64,
})
.collect();
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::Close) self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::Close)
.await?; .await?;
Ok(result) Ok(res_num.try_into().unwrap())
} }
pub async fn recording_parameters(&mut self) -> Result<Vec<u8>, InterfaceError> { pub async fn recording_parameters(&mut self) -> Result<Vec<u8>, InterfaceError> {
@ -1724,15 +1762,6 @@ pub fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec<u8> {
end[..8].to_vec() end[..8].to_vec()
} }
lazy_static! {
static ref DISC_FOR_WIRE: HashMap<WireFormat, DiscFormat> = HashMap::from([
(WireFormat::Pcm, DiscFormat::SPStereo),
(WireFormat::LP2, DiscFormat::LP2),
(WireFormat::L105kbps, DiscFormat::LP2),
(WireFormat::LP4, DiscFormat::LP4),
]);
}
pub struct EKBData { pub struct EKBData {
chains: [[u8; 16]; 2], chains: [[u8; 16]; 2],
depth: i32, depth: i32,
@ -1811,7 +1840,7 @@ impl MDTrack {
} }
pub fn frame_size(&self) -> usize { pub fn frame_size(&self) -> usize {
*FRAME_SIZE.get(&self.format).unwrap() self.format.frame_size() as usize
} }
pub fn chunk_size(&self) -> usize { pub fn chunk_size(&self) -> usize {
@ -1907,7 +1936,7 @@ impl<'a> MDSession<'a> {
) )
.await?; .await?;
let data_format = track.data_format(); let data_format = track.data_format();
let final_disc_format = disc_format.unwrap_or(*DISC_FOR_WIRE.get(&data_format).unwrap()); let final_disc_format = disc_format.unwrap_or(data_format.disc_for_wire());
let (track_index, uuid, ccid) = self let (track_index, uuid, ccid) = self
.md .md

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,19 @@
use crate::netmd::utils; use crate::netmd::utils;
use lazy_static::lazy_static;
use std::collections::hash_map::HashMap;
use thiserror::Error; use thiserror::Error;
lazy_static! { /// %b, w, d, q - explained above (can have endiannes overriden by '>' and '<' operators, f. ex. %>d %<q)
/// %b, w, d, q - explained above (can have endiannes overriden by '>' and '<' operators, f. ex. %>d %<q) /// %s - Uint8Array preceded by 2 bytes of length
/// %s - Uint8Array preceded by 2 bytes of length /// %x - Uint8Array preceded by 2 bytes of length
/// %x - Uint8Array preceded by 2 bytes of length /// %z - Uint8Array preceded by 1 byte of length
/// %z - Uint8Array preceded by 1 byte of length /// %* - raw Uint8Array
/// %* - raw Uint8Array /// %B - BCD-encoded 1-byte number
/// %B - BCD-encoded 1-byte number /// %W - BCD-encoded 2-byte number
/// %W - BCD-encoded 2-byte number static FORMAT_TYPE_LEN_DICT: phf::Map<char, i32> = phf::phf_map!{
static ref FORMAT_TYPE_LEN_DICT: HashMap<char, i32> = HashMap::from([ 'b' => 1, // byte
('b', 1), // byte 'w' => 2, // word
('w', 2), // word 'd' => 4, // doubleword
('d', 4), // doubleword 'q' => 8, // quadword
('q', 8), // quadword };
]);
}
const DEBUG: bool = false; const DEBUG: bool = false;
@ -37,11 +33,11 @@ pub enum ValueError {
} }
impl QueryValue { impl QueryValue {
pub fn from_array<const S: usize>(value: [u8; S]) -> Self { pub fn _from_array<const S: usize>(value: [u8; S]) -> Self {
Self::Array(value.to_vec()) Self::Array(value.to_vec())
} }
pub fn to_array<const S: usize>(&self) -> Result<[u8; S], ValueError> { pub fn _to_array<const S: usize>(&self) -> Result<[u8; S], ValueError> {
let mut array = [0u8; S]; let mut array = [0u8; S];
match self { match self {
QueryValue::Array(a) => { QueryValue::Array(a) => {
@ -78,6 +74,20 @@ impl QueryValue {
} }
} }
impl TryInto<i64> for QueryValue {
type Error = ValueError;
fn try_into(self) -> Result<i64, Self::Error> {
match self {
QueryValue::Number(a) => Ok(a),
_ => Err(ValueError::TypeMismatch {
expected: String::from("i64"),
actual: format!("{:?}", self)
}),
}
}
}
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum QueryError { pub enum QueryError {
#[error("unrecognized format character: `{0}`")] #[error("unrecognized format character: `{0}`")]

View file

@ -8,7 +8,7 @@ use unicode_normalization::UnicodeNormalization;
extern crate kana; extern crate kana;
use kana::*; use kana::*;
/// Sleep for a specified number of milliseconds 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"))]
std::thread::sleep(duration); std::thread::sleep(duration);
@ -75,29 +75,34 @@ pub fn get_bytes<const S: usize>(iterator: &mut IntoIter<u8>) -> Result<[u8; S],
Ok(bytes) Ok(bytes)
} }
pub fn length_after_encoding_to_jis(string: &str) -> usize { pub fn length_after_encoding_to_sjis(string: &str) -> usize {
let new_string = SHIFT_JIS.encode(string); let new_string = SHIFT_JIS.encode(string);
new_string.0.len() new_string.0.len()
} }
pub fn validate_shift_jis(sjis_string: Vec<u8>) -> bool { pub fn validate_sjis(sjis_string: Vec<u8>) -> bool {
let (_, _, had_errors) = SHIFT_JIS.decode(&sjis_string); let (_, _, had_errors) = SHIFT_JIS.decode(&sjis_string);
had_errors had_errors
} }
/// Ensure string contains only hardware allowed characters
fn check(string: String) -> Option<String> { fn check(string: String) -> Option<String> {
if MAPPINGS_HW.contains_key(&string) { if MAPPINGS_HW.contains_key(string.as_str()) {
return Some(MAPPINGS_HW.get(&string).unwrap().to_string()); return Some(MAPPINGS_HW.get(string.as_str()).unwrap().to_string());
} }
let mut ch = string.chars(); let mut ch = string.chars();
if (ch.next().unwrap() as u32) < 0x7f || ALLOWED_HW_KANA.contains(&string) { if (ch.next().unwrap() as u32) < 0x7f || ALLOWED_HW_KANA.contains(&string.as_str()) {
return Some(string); return Some(string);
} }
None None
} }
fn half_width_title_length(title: &str) {
}
pub fn sanitize_half_width_title(title: &str) -> Vec<u8> { pub fn sanitize_half_width_title(title: &str) -> Vec<u8> {
let mut string_title = wide2ascii(title); let mut string_title = wide2ascii(title);
string_title = nowidespace(&string_title); string_title = nowidespace(&string_title);
@ -115,7 +120,7 @@ pub fn sanitize_half_width_title(title: &str) -> Vec<u8> {
let sjis_string = SHIFT_JIS.encode(&new_title).0; let sjis_string = SHIFT_JIS.encode(&new_title).0;
if validate_shift_jis(sjis_string.clone().into()) { if validate_sjis(sjis_string.clone().into()) {
return agressive_sanitize_title(title).into(); return agressive_sanitize_title(title).into();
} }
@ -126,24 +131,25 @@ pub fn sanitize_half_width_title(title: &str) -> Vec<u8> {
pub fn sanitize_full_width_title(title: &str, just_remap: bool) -> Vec<u8> { pub fn sanitize_full_width_title(title: &str, just_remap: bool) -> Vec<u8> {
let new_title: String = title let new_title: String = title
.chars() .chars()
.map(|c| c.to_string())
.map(|character| { .map(|character| {
match MAPPINGS_JP.get(&character.to_string()) { match MAPPINGS_JP.get(character.to_string().as_str()) {
Some(string) => string.clone(), Some(string) => string,
None => character.to_string().clone(), None => character.as_str(),
} }
.to_string() .to_string()
}) })
.map(|character| { .map(|character| {
match MAPPINGS_RU.get(&character.to_string()) { match MAPPINGS_RU.get(character.as_str()) {
Some(string) => string.clone(), Some(string) => string,
None => character.to_string().clone(), None => character.as_str(),
} }
.to_string() .to_string()
}) })
.map(|character| { .map(|character| {
match MAPPINGS_DE.get(&character.to_string()) { match MAPPINGS_DE.get(character.as_str()) {
Some(string) => string.clone(), Some(string) => string,
None => character.to_string().clone(), None => character.as_str(),
} }
.to_string() .to_string()
}) })
@ -155,7 +161,7 @@ pub fn sanitize_full_width_title(title: &str, just_remap: bool) -> Vec<u8> {
let sjis_string = SHIFT_JIS.encode(&new_title).0; let sjis_string = SHIFT_JIS.encode(&new_title).0;
if validate_shift_jis(sjis_string.clone().into()) { if validate_sjis(sjis_string.clone().into()) {
return agressive_sanitize_title(title).into(); return agressive_sanitize_title(title).into();
} }
@ -173,9 +179,28 @@ pub fn agressive_sanitize_title(title: &str) -> String {
.into() .into()
} }
pub fn time_to_duration(time: &[u64]) -> std::time::Duration { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
assert_eq!(time.len(), 4); pub struct RawTime {
std::time::Duration::from_micros( pub hours: u64,
(time[0] * 3600000000) + (time[1] * 60000000) + (time[2] * 1000000) + (time[3] * 11600), pub minutes: u64,
) pub seconds: u64,
pub frames: u64,
}
impl Into<Duration> for RawTime {
fn into(self) -> std::time::Duration {
self.as_duration()
}
}
impl RawTime {
pub fn as_duration(&self) -> Duration {
std::time::Duration::from_micros(
(self.hours * 3600000000) + (self.minutes * 60000000) + (self.seconds * 1000000) + (self.frames * 11600),
)
}
pub fn as_frames(&self) -> u64 {
((self.hours * 60 + self.minutes) * 60 + self.seconds) * 512 + self.frames
}
} }