diff --git a/Cargo.toml b/Cargo.toml index c2fa763..7631bb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] bcd-numbers = "1.0.11" +encoding_rs = "0.8.33" nofmt = "1.0.0" once_cell = "1.18.0" rusb = "0.9.3" diff --git a/src/main.rs b/src/main.rs index 12027d7..ca822c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,20 +32,10 @@ fn main() { player_controller.net_md_device.device_name().unwrap() ); - println!("{:?}", player_controller.get_position()); - println!("{:?}", player_controller.go_to_time(0, 0, 2, 0, 0)); - let _ = player_controller.play(); - thread::sleep(Duration::from_secs(5)); - println!("{:?}", player_controller.get_position()); - println!("{:?}", player_controller.go_to_time(10, 0, 1, 15, 0)); - println!("{:?}", player_controller.get_position()); - - thread::sleep(Duration::from_secs(10)); - println!("{:?}", player_controller.get_position()); - - thread::sleep(Duration::from_secs(5)); - - let _ = player_controller.stop(); + println!("Disc Flags? {:?}", player_controller.disc_flags()); + println!("Track Count: {:?}", player_controller.track_count()); + println!("Disc Title: {:?}", player_controller.disc_title(false)); + println!("Track Group List: {:?}", player_controller.track_group_list()); /* let mut request: [u8; 19] = [0x00, 0x18, 0x06, 0x02, 0x20, 0x18, diff --git a/src/netmd/base.rs b/src/netmd/base.rs index 97decd0..521df21 100644 --- a/src/netmd/base.rs +++ b/src/netmd/base.rs @@ -160,7 +160,6 @@ impl NetMD { }; let length_bytes = [poll_result[2], poll_result[3]]; - println!("Poll result: {}", u16::from_le_bytes(length_bytes)); Ok((u16::from_le_bytes(length_bytes), poll_result)) } @@ -209,19 +208,26 @@ impl NetMD { self._read_reply(false, override_length) } - pub fn read_factory_reply(&self, override_length: Option) -> Result, Box> { + pub fn read_factory_reply( + &self, + override_length: Option, + ) -> Result, Box> { self._read_reply(true, override_length) } /// Poll to see if a message is ready, /// and if so, recieve it - fn _read_reply(&self, use_factory_command: bool, override_length: Option) -> Result, Box> { + fn _read_reply( + &self, + use_factory_command: bool, + override_length: Option, + ) -> Result, Box> { let mut length = self.poll()?.0; let mut current_attempt = 0; while length == 0 { let sleep_time = Self::READ_REPLY_RETRY_INTERVAL as u64 - * (u64::pow(2, current_attempt as u32 / 10) - 1); + * (u64::pow(2, current_attempt as u32 / 10) - 1); std::thread::sleep(std::time::Duration::from_millis(sleep_time)); length = self.poll()?.0; @@ -230,7 +236,7 @@ impl NetMD { match override_length { Some(value) => length = value as u16, - None => () + None => (), } let request = match use_factory_command { diff --git a/src/netmd/interface.rs b/src/netmd/interface.rs index ba308b9..c7328d9 100644 --- a/src/netmd/interface.rs +++ b/src/netmd/interface.rs @@ -1,7 +1,10 @@ use crate::netmd::utils; use crate::NetMD; +use encoding_rs::*; +use std::collections::HashMap; use std::error::Error; -use std::fmt; + +use super::utils::half_width_to_full_width_range; #[derive(Copy, Clone)] enum Action { @@ -411,15 +414,33 @@ impl NetMDInterface { } fn acquire(&self) -> Result<(), Box> { - let mut query = vec![0xff, 0x01, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; + let mut query = vec![ + 0xff, 0x01, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, + ]; let reply = self.send_query(&mut query, false, false)?; - utils::check_result(reply, &[0xff, 0x01, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) + utils::check_result( + reply, + &[ + 0xff, 0x01, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, + ], + ) } fn release(&self) -> Result<(), Box> { - let mut query = vec![0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; + let mut query = vec![ + 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, + ]; let reply = self.send_query(&mut query, false, false)?; - utils::check_result(reply, &[0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) + utils::check_result( + reply, + &[ + 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, + ], + ) } fn status(&self) -> Result, Box> { @@ -504,18 +525,18 @@ impl NetMDInterface { self.playback_status_query([0x88, 0x02], [0x88, 0x06]) } - pub fn get_position(&self) -> Result<[u16; 5], Box> { + pub fn position(&self) -> Result<[u16; 5], Box> { self.change_descriptor_state(Descriptor::OperatingStatusBlock, DescriptorAction::OpenRead); let mut query = vec![ 0x18, 0x09, 0x80, 0x01, 0x04, 0x30, 0x88, 0x02, 0x00, 0x30, 0x88, 0x05, 0x00, 0x30, - 0x00, 0x03, 0x00, 0x30, 0x00, 0x02, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x03, 0x00, 0x30, 0x00, 0x02, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let reply = match self.send_query(&mut query, false, false) { Ok(result) => result, Err(e) if e.to_string() == "Rejected" => Vec::new(), - Err(e) => return Err(e) + Err(e) => return Err(e), }; let track_number = u16::from_be_bytes([reply[35], reply[36]]); @@ -525,7 +546,13 @@ impl NetMDInterface { let second = utils::byte_from_bcd(reply[39])?; let frame = utils::byte_from_bcd(reply[40])?; - let final_result = [track_number, hour as u16, minute as u16, second as u16, frame as u16]; + let final_result = [ + track_number, + hour as u16, + minute as u16, + second as u16, + frame as u16, + ]; self.change_descriptor_state(Descriptor::OperatingStatusBlock, DescriptorAction::Close); @@ -542,13 +569,14 @@ impl NetMDInterface { let mut query = vec![0x18, 0xc1, 0xff, 0x60, 0x00]; match self.send_query(&mut query, true, false) { Ok(_) => Ok(true), - Err(error) => Err(error) + Err(error) => Err(error), } } + /* Track control */ + pub fn go_to_track(&self, track_number: u16) -> Result> { - let mut query = vec![0x18, 0x50, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, - 0b00, 0b00]; + let mut query = vec![0x18, 0x50, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0b00, 0b00]; let bytes = track_number.to_le_bytes(); @@ -560,9 +588,17 @@ impl NetMDInterface { Ok(u16::from_be_bytes([reply[8], reply[9]])) } - pub fn go_to_time(&self, track_number: u16, hour: u8, minute: u8, second: u8, frame: u8) -> Result> { - let mut query = vec![0x18, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0b00, 0b00, 0b00, 0b00, 0b00, 0b00]; + pub fn go_to_time( + &self, + track_number: u16, + hour: u8, + minute: u8, + second: u8, + frame: u8, + ) -> Result> { + let mut query = vec![ + 0x18, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0b00, 0b00, 0b00, 0b00, 0b00, 0b00, + ]; let bytes = track_number.to_le_bytes(); query[8] = bytes[1]; @@ -579,8 +615,7 @@ impl NetMDInterface { } fn _track_change(&self, direction: Track) -> Result<(), Box> { - let mut query = vec![0x18, 0x50, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, - 0b00, 0b00]; + let mut query = vec![0x18, 0x50, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0b00, 0b00]; let direction_number = direction as u16; let direction_bytes = direction_number.to_le_bytes(); @@ -604,4 +639,171 @@ impl NetMDInterface { pub fn restart_track(&self) -> Result<(), Box> { self._track_change(Track::Next) } + + /* Content access and control */ + pub fn erase_disc(&self) -> Result<(), Box> { + let mut query = vec![0x18, 0x40, 0xff, 0x00, 0x00]; + let reply = self.send_query(&mut query, false, false)?; + utils::check_result(reply, &[0x18, 0x40, 0x00, 0x00, 0x00]) + } + + // TODO: Ensure this is returning the correct value, it + // looks like it actually might be a 16 bit integer + pub fn disc_flags(&self) -> Result> { + self.change_descriptor_state(Descriptor::RootTD, DescriptorAction::OpenRead); + let mut query = vec![ + 0x18, 0x06, 0x01, 0x10, 0x10, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0x0b, + ]; + + let reply = self.send_query(&mut query, false, false)?; + + let flags = reply[12]; + self.change_descriptor_state(Descriptor::RootTD, DescriptorAction::Close); + + Ok(flags) + } + + pub fn track_count(&self) -> Result> { + self.change_descriptor_state(Descriptor::AudioContentsTD, DescriptorAction::OpenRead); + + let mut query = vec![ + 0x18, 0x06, 0x02, 0x10, 0x10, 0x01, 0x30, 0x00, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + + let reply = self.send_query(&mut query, false, false)?; + + let track_count = reply[24]; + + self.change_descriptor_state(Descriptor::AudioContentsTD, DescriptorAction::Close); + + Ok(track_count) + } + + fn _disc_title(&self, wchar: bool) -> Result> { + self.change_descriptor_state(Descriptor::AudioContentsTD, DescriptorAction::OpenRead); + self.change_descriptor_state(Descriptor::DiscTitleTD, DescriptorAction::OpenRead); + + let mut done: u16 = 0; + let mut remaining: u16 = 0; + let mut total = 1; + let mut result: Vec = Vec::new(); + let mut chunksize = 0; + let mut chunk = String::new(); + + while done < total { + let mut query = vec![ + 0x18, 0x06, 0x02, 0x20, 0x18, 0x01, 0x00, 0b00, 0x30, 0x00, 0x0a, 0x00, 0xff, 0x00, + 0b00, 0b00, 0b00, 0b00, + ]; + + query[7] = match wchar { + true => 1, + false => 0, + }; + + let remain_bytes = remaining.to_le_bytes(); + query[14] = remain_bytes[0]; + query[15] = remain_bytes[1]; + + let done_bytes = done.to_le_bytes(); + query[16] = done_bytes[0]; + query[17] = done_bytes[1]; + + let reply = self.send_query(&mut query, false, false)?; + + if remaining == 0 { + chunksize = u16::from_le_bytes([reply[13], reply[14]]); + total = u16::from_le_bytes([reply[22], reply[23]]); + + chunk = SHIFT_JIS.decode(&reply[25..]).0.into(); + + chunksize -= 6; + } else { + chunksize = u16::from_le_bytes([reply[13], reply[14]]); + chunk = SHIFT_JIS.decode(&reply[18..]).0.into(); + } + + result.push(chunk); + done += chunksize; + remaining = total - done; + } + + let final_result = result.join(""); + + Ok(final_result) + } + + pub fn disc_title(&self, wchar: bool) -> Result> { + let mut title = self._disc_title(wchar)?; + + let delim = match wchar { + true => "//", + false => "//", + }; + + let title_marker = match wchar { + true => "0;", + false => "0;", + }; + + if title.ends_with(delim) { + let first_entry = title.split(delim).collect::>()[0]; + if first_entry.starts_with(title_marker) { + title = first_entry[title_marker.len()..].to_string(); + } else { + title = String::new(); + } + } + + Ok(title) + } + + pub fn track_group_list(&self) -> Result<(), Box> { + let raw_title = self._disc_title(false)?; + let group_list = raw_title.split("//"); + let mut track_dict: HashMap = HashMap::new(); + let track_count = self.track_count(); + let result: Vec<(String, String, u16)> = Vec::new(); + + let raw_full_title = self._disc_title(true)?; + + let mut full_width_group_list = raw_full_title.split("//"); + + for (i, group) in group_list.enumerate() { + if group == "" { + continue; + } + + if group.starts_with("0;") || group.find(";") == None || raw_title.find("//") == None { + continue; + } + + let track_range: String = match group.split_once(";") { + Some(string) => string.0.to_string(), + None => return Err("No groups were found".into()), + }; + if track_range.len() == 0 { + continue; + } + + let group_name = &group[track_range.len() + 1..]; + + println!("{}", group_name); + + let full_width_range = utils::half_width_to_full_width_range(track_range); + + //println!("{:?}", full_width_group_list); + + let full_width_group_name = full_width_group_list + .find(|n| n.starts_with(&full_width_range)) + .unwrap() + .split_once(";") + .unwrap() + .1; + + println!("{}", full_width_group_name); + } + Ok(()) + } } diff --git a/src/netmd/utils.rs b/src/netmd/utils.rs index c0467cf..732409e 100644 --- a/src/netmd/utils.rs +++ b/src/netmd/utils.rs @@ -1,3 +1,4 @@ +use std::collections::hash_map::HashMap; use std::error::Error; pub fn check_result(result: Vec, expected: &[u8]) -> Result<(), Box> { @@ -12,11 +13,11 @@ pub fn byte_from_bcd(byte: u8) -> Result> { let lower = byte & 0x0F; if upper >= 10 { - return Err("Upper nybble out of range [0..9]".into()) + return Err("Upper nybble out of range [0..9]".into()); } if lower >= 10 { - return Err("Lower nybble out of range [0..9]".into()) + return Err("Lower nybble out of range [0..9]".into()); } Ok(upper * 10 + lower) @@ -33,3 +34,26 @@ pub fn bcd_from_byte(byte: u8) -> Result> { Ok(new_byte) } + +pub fn half_width_to_full_width_range(range: String) -> String { + let mappings: HashMap = HashMap::from([ + ('0', '0'), + ('1', '1'), + ('2', '2'), + ('3', '3'), + ('4', '4'), + ('5', '5'), + ('6', '6'), + ('7', '7'), + ('8', '8'), + ('9', '9'), + ('-', '-'), + ('/', '/'), + (';', ';'), + ]); + + range + .chars() + .map(|char| mappings.get(&char).unwrap()) + .collect() +}