From 628c1afd799d47fc877c8f78a9bb84903f9f1ce6 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Tue, 26 Sep 2023 03:03:48 -0500 Subject: [PATCH] Finished implementation of string sanitization functions --- minidisc-rs/Cargo.toml | 4 +- minidisc-rs/src/netmd/interface.rs | 13 +- minidisc-rs/src/netmd/mappings.rs | 289 ++++++++++++++++++++++++++++- minidisc-rs/src/netmd/utils.rs | 83 +++++++-- 4 files changed, 366 insertions(+), 23 deletions(-) diff --git a/minidisc-rs/Cargo.toml b/minidisc-rs/Cargo.toml index 320f3d2..5ceea67 100644 --- a/minidisc-rs/Cargo.toml +++ b/minidisc-rs/Cargo.toml @@ -12,10 +12,12 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +diacritics = "0.2.0" encoding_rs = "0.8.33" nofmt = "1.0.0" once_cell = "1.18.0" +regex = "1.9.5" rusb = "0.9.3" -translit = "0.5.0" unicode-jp = "0.4.0" +unicode-normalization = "0.1.22" diff --git a/minidisc-rs/src/netmd/interface.rs b/minidisc-rs/src/netmd/interface.rs index 76b4f66..68487a2 100644 --- a/minidisc-rs/src/netmd/interface.rs +++ b/minidisc-rs/src/netmd/interface.rs @@ -1,7 +1,8 @@ use crate::netmd::base; use crate::netmd::query_utils::{format_query, scan_query, QueryValue}; 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_jis, + sanitize_full_width_title, sanitize_half_width_title }; use encoding_rs::*; use rusb; @@ -929,10 +930,10 @@ impl NetMDInterface { .into()) } - pub fn set_disc_title(&self, title: String, wchar: bool) -> Result> { + pub fn set_disc_title(&self, title: String, wchar: bool) -> Result<(), Box> { let current_title = self._disc_title(wchar)?; if current_title == title { - return Ok(current_title); + return Ok(()); } let new_title: Vec; @@ -944,7 +945,7 @@ impl NetMDInterface { 1 }, false => { - new_title = Vec::new(); + new_title = sanitize_half_width_title(title); 0 }, }; @@ -968,7 +969,7 @@ impl NetMDInterface { ], )?; - let reply = self.send_query(&mut query, false, false); + let _ = self.send_query(&mut query, false, false); if self.net_md_device.vendor_id() == &0x04dd { self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::Close) @@ -978,6 +979,6 @@ impl NetMDInterface { self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close); } - Ok(String::from_utf8(sanitize_full_width_title(&title, true)).unwrap()) + Ok(()) } } diff --git a/minidisc-rs/src/netmd/mappings.rs b/minidisc-rs/src/netmd/mappings.rs index 0b0568d..10e6937 100644 --- a/minidisc-rs/src/netmd/mappings.rs +++ b/minidisc-rs/src/netmd/mappings.rs @@ -1,7 +1,7 @@ use once_cell::sync::Lazy; use std::collections::HashMap; -pub const MAPPINGS_JP: Lazy> = Lazy::new(|| {vec![ +pub const MAPPINGS_JP: Lazy> = Lazy::new(|| {[ ("!".to_string(), "!".to_string()), ("\"".to_string(), """.to_string()), ("#".to_string(), "#".to_string()), @@ -193,7 +193,7 @@ pub const MAPPINGS_JP: Lazy> = Lazy::new(|| {vec![ ("、".to_string(), "、".to_string()) ].into_iter().collect()}); -pub const MAPPINGS_RU: Lazy> = Lazy::new(|| {vec![ +pub const MAPPINGS_RU: Lazy> = Lazy::new(|| {[ ("а".to_string(), "a".to_string()), ("б".to_string(), "b".to_string()), ("в".to_string(), "v".to_string()), @@ -262,7 +262,7 @@ pub const MAPPINGS_RU: Lazy> = Lazy::new(|| {vec![ ("Я".to_string(), "Ia".to_string()) ].into_iter().collect()}); -pub const MAPPINGS_DE: Lazy> = Lazy::new(|| {vec![ +pub const MAPPINGS_DE: Lazy> = Lazy::new(|| {[ ("Ä".to_string(), "Ae".to_string()), ("ä".to_string(), "ae".to_string()), ("Ö".to_string(), "Oe".to_string()), @@ -271,3 +271,286 @@ pub const MAPPINGS_DE: Lazy> = Lazy::new(|| {vec![ ("ü".to_string(), "ue".to_string()), ("ß".to_string(), "ss".to_string()) ].into_iter().collect()}); + +pub const MAPPINGS_HW: Lazy> = Lazy::new(|| {[ + ("-".to_string(),"-".to_string()), + ("ー".to_string(),"-".to_string()), + ("ァ".to_string(),"ァ".to_string()), + ("ア".to_string(),"ア".to_string()), + ("ィ".to_string(),"ィ".to_string()), + ("イ".to_string(),"イ".to_string()), + ("ゥ".to_string(),"ゥ".to_string()), + ("ウ".to_string(),"ウ".to_string()), + ("ェ".to_string(),"ェ".to_string()), + ("エ".to_string(),"エ".to_string()), + ("ォ".to_string(),"ォ".to_string()), + ("オ".to_string(),"オ".to_string()), + ("カ".to_string(),"カ".to_string()), + ("ガ".to_string(),"ガ".to_string()), + ("キ".to_string(),"キ".to_string()), + ("ギ".to_string(),"ギ".to_string()), + ("ク".to_string(),"ク".to_string()), + ("グ".to_string(),"グ".to_string()), + ("ケ".to_string(),"ケ".to_string()), + ("ゲ".to_string(),"ゲ".to_string()), + ("コ".to_string(),"コ".to_string()), + ("ゴ".to_string(),"ゴ".to_string()), + ("サ".to_string(),"サ".to_string()), + ("ザ".to_string(),"ザ".to_string()), + ("シ".to_string(),"シ".to_string()), + ("ジ".to_string(),"ジ".to_string()), + ("ス".to_string(),"ス".to_string()), + ("ズ".to_string(),"ズ".to_string()), + ("セ".to_string(),"セ".to_string()), + ("ゼ".to_string(),"ゼ".to_string()), + ("ソ".to_string(),"ソ".to_string()), + ("ゾ".to_string(),"ゾ".to_string()), + ("タ".to_string(),"タ".to_string()), + ("ダ".to_string(),"ダ".to_string()), + ("チ".to_string(),"チ".to_string()), + ("ヂ".to_string(),"ヂ".to_string()), + ("ッ".to_string(),"ッ".to_string()), + ("ツ".to_string(),"ツ".to_string()), + ("ヅ".to_string(),"ヅ".to_string()), + ("テ".to_string(),"テ".to_string()), + ("デ".to_string(),"デ".to_string()), + ("ト".to_string(),"ト".to_string()), + ("ド".to_string(),"ド".to_string()), + ("ナ".to_string(),"ナ".to_string()), + ("ニ".to_string(),"ニ".to_string()), + ("ヌ".to_string(),"ヌ".to_string()), + ("ネ".to_string(),"ネ".to_string()), + ("ノ".to_string(),"ノ".to_string()), + ("ハ".to_string(),"ハ".to_string()), + ("バ".to_string(),"バ".to_string()), + ("パ".to_string(),"パ".to_string()), + ("ヒ".to_string(),"ヒ".to_string()), + ("ビ".to_string(),"ビ".to_string()), + ("ピ".to_string(),"ピ".to_string()), + ("フ".to_string(),"フ".to_string()), + ("ブ".to_string(),"ブ".to_string()), + ("プ".to_string(),"プ".to_string()), + ("ヘ".to_string(),"ヘ".to_string()), + ("ベ".to_string(),"ベ".to_string()), + ("ペ".to_string(),"ペ".to_string()), + ("ホ".to_string(),"ホ".to_string()), + ("ボ".to_string(),"ボ".to_string()), + ("ポ".to_string(),"ポ".to_string()), + ("マ".to_string(),"マ".to_string()), + ("ミ".to_string(),"ミ".to_string()), + ("ム".to_string(),"ム".to_string()), + ("メ".to_string(),"メ".to_string()), + ("モ".to_string(),"モ".to_string()), + ("ャ".to_string(),"ャ".to_string()), + ("ヤ".to_string(),"ヤ".to_string()), + ("ュ".to_string(),"ュ".to_string()), + ("ユ".to_string(),"ユ".to_string()), + ("ョ".to_string(),"ョ".to_string()), + ("ヨ".to_string(),"ヨ".to_string()), + ("ラ".to_string(),"ラ".to_string()), + ("リ".to_string(),"リ".to_string()), + ("ル".to_string(),"ル".to_string()), + ("レ".to_string(),"レ".to_string()), + ("ロ".to_string(),"ロ".to_string()), + ("ワ".to_string(),"ワ".to_string()), + ("ヲ".to_string(),"ヲ".to_string()), + ("ン".to_string(),"ン".to_string()), + ("ー".to_string(),"-".to_string()), + ("ヮ".to_string(),"ヮ".to_string()), + ("ヰ".to_string(),"ヰ".to_string()), + ("ヱ".to_string(),"ヱ".to_string()), + ("ヵ".to_string(),"ヵ".to_string()), + ("ヶ".to_string(),"ヶ".to_string()), + ("ヴ".to_string(),"ヴ".to_string()), + ("ヽ".to_string(),"ヽ".to_string()), + ("ヾ".to_string(),"ヾ".to_string()), + ("・".to_string(),"・".to_string()), + ("「".to_string(),"「".to_string()), + ("」".to_string(),"」".to_string()), + ("。".to_string(),"。".to_string()), + ("、".to_string(),"、".to_string()), + ("!".to_string(),"!".to_string()), + (""".to_string(),"\"".to_string()), + ("#".to_string(),"#".to_string()), + ("$".to_string(),"$".to_string()), + ("%".to_string(),"%".to_string()), + ("&".to_string(),"&".to_string()), + ("'".to_string(),"'".to_string()), + ("(".to_string(),"(".to_string()), + (")".to_string(),")".to_string()), + ("*".to_string(),"*".to_string()), + ("+".to_string(),"+".to_string()), + (",".to_string(),",".to_string()), + (".".to_string(),".".to_string()), + ("/".to_string(),"/".to_string()), + (":".to_string(),":".to_string()), + (";".to_string(),";".to_string()), + ("<".to_string(),"<".to_string()), + ("=".to_string(),"=".to_string()), + (">".to_string(),">".to_string()), + ("?".to_string(),"?".to_string()), + ("@".to_string(),"@".to_string()), + ("A".to_string(),"A".to_string()), + ("B".to_string(),"B".to_string()), + ("C".to_string(),"C".to_string()), + ("D".to_string(),"D".to_string()), + ("E".to_string(),"E".to_string()), + ("F".to_string(),"F".to_string()), + ("G".to_string(),"G".to_string()), + ("H".to_string(),"H".to_string()), + ("I".to_string(),"I".to_string()), + ("J".to_string(),"J".to_string()), + ("K".to_string(),"K".to_string()), + ("L".to_string(),"L".to_string()), + ("M".to_string(),"M".to_string()), + ("N".to_string(),"N".to_string()), + ("O".to_string(),"O".to_string()), + ("P".to_string(),"P".to_string()), + ("Q".to_string(),"Q".to_string()), + ("R".to_string(),"R".to_string()), + ("S".to_string(),"S".to_string()), + ("T".to_string(),"T".to_string()), + ("U".to_string(),"U".to_string()), + ("V".to_string(),"V".to_string()), + ("W".to_string(),"W".to_string()), + ("X".to_string(),"X".to_string()), + ("Y".to_string(),"Y".to_string()), + ("Z".to_string(),"Z".to_string()), + ("[".to_string(),"[".to_string()), + ("\".to_string(),"\\".to_string()), + ("]".to_string(),"]".to_string()), + ("^".to_string(),"^".to_string()), + ("_".to_string(),"_".to_string()), + ("`".to_string(),"`".to_string()), + ("a".to_string(),"a".to_string()), + ("b".to_string(),"b".to_string()), + ("c".to_string(),"c".to_string()), + ("d".to_string(),"d".to_string()), + ("e".to_string(),"e".to_string()), + ("f".to_string(),"f".to_string()), + ("g".to_string(),"g".to_string()), + ("h".to_string(),"h".to_string()), + ("i".to_string(),"i".to_string()), + ("j".to_string(),"j".to_string()), + ("k".to_string(),"k".to_string()), + ("l".to_string(),"l".to_string()), + ("m".to_string(),"m".to_string()), + ("n".to_string(),"n".to_string()), + ("o".to_string(),"o".to_string()), + ("p".to_string(),"p".to_string()), + ("q".to_string(),"q".to_string()), + ("r".to_string(),"r".to_string()), + ("s".to_string(),"s".to_string()), + ("t".to_string(),"t".to_string()), + ("u".to_string(),"u".to_string()), + ("v".to_string(),"v".to_string()), + ("w".to_string(),"w".to_string()), + ("x".to_string(),"x".to_string()), + ("y".to_string(),"y".to_string()), + ("z".to_string(),"z".to_string()), + ("{".to_string(),"{".to_string()), + ("|".to_string(),"|".to_string()), + ("}".to_string(),"}".to_string()), + ("~".to_string(),"~".to_string()), + (" ".to_string()," ".to_string()), + ("0".to_string(),"0".to_string()), + ("1".to_string(),"1".to_string()), + ("2".to_string(),"2".to_string()), + ("3".to_string(),"3".to_string()), + ("4".to_string(),"4".to_string()), + ("5".to_string(),"5".to_string()), + ("6".to_string(),"6".to_string()), + ("7".to_string(),"7".to_string()), + ("8".to_string(),"8".to_string()), + ("9".to_string(),"9".to_string()), + ("ぁ".to_string(),"ァ".to_string()), + ("あ".to_string(),"ア".to_string()), + ("ぃ".to_string(),"ィ".to_string()), + ("い".to_string(),"イ".to_string()), + ("ぅ".to_string(),"ゥ".to_string()), + ("う".to_string(),"ウ".to_string()), + ("ぇ".to_string(),"ェ".to_string()), + ("え".to_string(),"エ".to_string()), + ("ぉ".to_string(),"ォ".to_string()), + ("お".to_string(),"オ".to_string()), + ("か".to_string(),"カ".to_string()), + ("が".to_string(),"ガ".to_string()), + ("き".to_string(),"キ".to_string()), + ("ぎ".to_string(),"ギ".to_string()), + ("く".to_string(),"ク".to_string()), + ("ぐ".to_string(),"グ".to_string()), + ("け".to_string(),"ケ".to_string()), + ("げ".to_string(),"ゲ".to_string()), + ("こ".to_string(),"コ".to_string()), + ("ご".to_string(),"ゴ".to_string()), + ("さ".to_string(),"サ".to_string()), + ("ざ".to_string(),"ザ".to_string()), + ("し".to_string(),"シ".to_string()), + ("じ".to_string(),"ジ".to_string()), + ("す".to_string(),"ス".to_string()), + ("ず".to_string(),"ズ".to_string()), + ("せ".to_string(),"セ".to_string()), + ("ぜ".to_string(),"ゼ".to_string()), + ("そ".to_string(),"ソ".to_string()), + ("ぞ".to_string(),"ゾ".to_string()), + ("た".to_string(),"タ".to_string()), + ("だ".to_string(),"ダ".to_string()), + ("ち".to_string(),"チ".to_string()), + ("ぢ".to_string(),"ヂ".to_string()), + ("っ".to_string(),"ッ".to_string()), + ("つ".to_string(),"ツ".to_string()), + ("づ".to_string(),"ヅ".to_string()), + ("て".to_string(),"テ".to_string()), + ("で".to_string(),"デ".to_string()), + ("と".to_string(),"ト".to_string()), + ("ど".to_string(),"ド".to_string()), + ("な".to_string(),"ナ".to_string()), + ("に".to_string(),"ニ".to_string()), + ("ぬ".to_string(),"ヌ".to_string()), + ("ね".to_string(),"ネ".to_string()), + ("の".to_string(),"ノ".to_string()), + ("は".to_string(),"ハ".to_string()), + ("ば".to_string(),"バ".to_string()), + ("ぱ".to_string(),"パ".to_string()), + ("ひ".to_string(),"ヒ".to_string()), + ("び".to_string(),"ビ".to_string()), + ("ぴ".to_string(),"ピ".to_string()), + ("ふ".to_string(),"フ".to_string()), + ("ぶ".to_string(),"ブ".to_string()), + ("ぷ".to_string(),"プ".to_string()), + ("へ".to_string(),"ヘ".to_string()), + ("べ".to_string(),"ベ".to_string()), + ("ぺ".to_string(),"ペ".to_string()), + ("ほ".to_string(),"ホ".to_string()), + ("ぼ".to_string(),"ボ".to_string()), + ("ぽ".to_string(),"ポ".to_string()), + ("ま".to_string(),"マ".to_string()), + ("み".to_string(),"ミ".to_string()), + ("む".to_string(),"ム".to_string()), + ("め".to_string(),"メ".to_string()), + ("も".to_string(),"モ".to_string()), + ("ゃ".to_string(),"ャ".to_string()), + ("や".to_string(),"ヤ".to_string()), + ("ゅ".to_string(),"ュ".to_string()), + ("ゆ".to_string(),"ユ".to_string()), + ("ょ".to_string(),"ョ".to_string()), + ("よ".to_string(),"ヨ".to_string()), + ("ら".to_string(),"ラ".to_string()), + ("り".to_string(),"リ".to_string()), + ("る".to_string(),"ル".to_string()), + ("れ".to_string(),"レ".to_string()), + ("ろ".to_string(),"ロ".to_string()), + ("わ".to_string(),"ワ".to_string()), + ("を".to_string(),"ヲ".to_string()), + ("ん".to_string(),"ン".to_string()), + ("ゎ".to_string(),"ヮ".to_string()), + ("ゐ".to_string(),"ヰ".to_string()), + ("ゑ".to_string(),"ヱ".to_string()), + ("ゕ".to_string(),"ヵ".to_string()), + ("ゖ".to_string(),"ヶ".to_string()), + ("ゔ".to_string(),"ヴ".to_string()), + ("ゝ".to_string(),"ヽ".to_string()), + ("ゞ".to_string(),"ヾ".to_string()), +].into_iter().collect()}); + +pub const ALLOWED_HW_KANA: Lazy> = Lazy::new(|| {MAPPINGS_HW.values().cloned().collect()}); diff --git a/minidisc-rs/src/netmd/utils.rs b/minidisc-rs/src/netmd/utils.rs index 4dc0452..58db065 100644 --- a/minidisc-rs/src/netmd/utils.rs +++ b/minidisc-rs/src/netmd/utils.rs @@ -1,8 +1,12 @@ +use crate::netmd::mappings::{ALLOWED_HW_KANA, MAPPINGS_DE, MAPPINGS_HW, MAPPINGS_JP, MAPPINGS_RU}; +use diacritics; use encoding_rs::SHIFT_JIS; -use kana::{ascii2wide, combine, half2kana}; +use regex::Regex; use std::collections::hash_map::HashMap; -use std::error::Error; -use crate::netmd::mappings::{MAPPINGS_JP, MAPPINGS_RU, MAPPINGS_DE}; +use unicode_normalization::UnicodeNormalization; + +extern crate kana; +use kana::*; pub fn bcd_to_int(mut bcd: i32) -> i32 { let mut value = 0; @@ -76,16 +80,51 @@ pub fn length_after_encoding_to_jis(string: &String) -> usize { new_string.0.len() } -pub fn validate_shift_jis(sjis_string: &Vec) -> Result<(), Box> { - let (_, _, had_errors) = SHIFT_JIS.decode(sjis_string); +pub fn validate_shift_jis(sjis_string: Vec) -> bool { + let (_, _, had_errors) = SHIFT_JIS.decode(&sjis_string); if had_errors { - Err("Not valid SHIFT-JIS".into()) + true } else { - Ok(()) + false } } +fn check(string: String) -> Option { + if MAPPINGS_HW.contains_key(&string) { + return Some(MAPPINGS_HW.get(&string).unwrap().to_string()); + } + let mut ch = string.chars(); + if (ch.next().unwrap() as u32) < 0x7f || ALLOWED_HW_KANA.contains(&string) { + return Some(string); + } + None +} + +pub fn sanitize_half_width_title(mut title: String) -> Vec { + title = wide2ascii(&title); + title = nowidespace(&title); + title = hira2kata(&title); + title = combine(&title); + + let new_title: String = title + .chars() + .map(|c| { + check(c.to_string()).unwrap_or( + check(diacritics::remove_diacritics(&c.to_string())).unwrap_or(" ".to_string()), + ) + }) + .collect(); + + let sjis_string = SHIFT_JIS.encode(&new_title).0; + + if validate_shift_jis(sjis_string.clone().into()) { + return agressive_sanitize_title(&title).into(); + } + + return sjis_string.into(); +} + // TODO: This function is bad, probably should do the string sanitization in the frontend pub fn sanitize_full_width_title(title: &String, just_remap: bool) -> Vec { let new_title: String = title @@ -93,20 +132,23 @@ pub fn sanitize_full_width_title(title: &String, just_remap: bool) -> Vec { .map(|character| { match MAPPINGS_JP.get(&character.to_string()) { Some(string) => string.clone(), - None => character.to_string().clone() - }.to_string() + None => character.to_string().clone(), + } + .to_string() }) .map(|character| { match MAPPINGS_RU.get(&character.to_string()) { Some(string) => string.clone(), - None => character.to_string().clone() - }.to_string() + None => character.to_string().clone(), + } + .to_string() }) .map(|character| { match MAPPINGS_DE.get(&character.to_string()) { Some(string) => string.clone(), - None => character.to_string().clone() - }.to_string() + None => character.to_string().clone(), + } + .to_string() }) .collect::(); @@ -116,5 +158,20 @@ pub fn sanitize_full_width_title(title: &String, just_remap: bool) -> Vec { let sjis_string = SHIFT_JIS.encode(&new_title).0; + if validate_shift_jis(sjis_string.clone().into()) { + return agressive_sanitize_title(&title).into(); + } + return sjis_string.into(); } + +pub fn agressive_sanitize_title(title: &String) -> String { + let re = Regex::new(r"[^\x00-\x7F]").unwrap(); + re.replace_all( + &diacritics::remove_diacritics(title) + .nfd() + .collect::(), + "", + ) + .into() +}