From 02338a69a67f07dd6747dfc11c79288c01655f4b Mon Sep 17 00:00:00 2001 From: G2-Games Date: Wed, 27 Sep 2023 08:36:53 -0500 Subject: [PATCH] Added more functions to `interface.rs` --- minidisc-rs/src/netmd/interface.rs | 194 ++++++++++++++++++++++++++- minidisc-rs/src/netmd/query_utils.rs | 3 +- minidisc-rs/src/netmd/utils.rs | 5 + src/main.rs | 20 ++- 4 files changed, 212 insertions(+), 10 deletions(-) diff --git a/minidisc-rs/src/netmd/interface.rs b/minidisc-rs/src/netmd/interface.rs index 68487a2..03725a4 100644 --- a/minidisc-rs/src/netmd/interface.rs +++ b/minidisc-rs/src/netmd/interface.rs @@ -2,7 +2,7 @@ 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, sanitize_half_width_title + sanitize_full_width_title, sanitize_half_width_title, time_to_frames }; use encoding_rs::*; use rusb; @@ -48,7 +48,8 @@ impl WireFormat { } } -enum Encoding { +#[derive(Debug)] +pub enum Encoding { SP = 0x90, LP2 = 0x92, LP4 = 0x93, @@ -318,6 +319,7 @@ impl NetMDInterface { let _ = self.send_query(&mut query, false, false); } + /// Send a query to the NetMD player fn send_query( &self, query: &mut Vec, @@ -664,14 +666,17 @@ impl NetMDInterface { Ok(()) } + /// Change to the next track (skip forward) pub fn next_track(&self) -> Result<(), Box> { self._track_change(Track::Next) } + /// Change to the next track (skip back) pub fn previous_track(&self) -> Result<(), Box> { self._track_change(Track::Next) } + /// Change to the next track (skip to beginning of track) pub fn restart_track(&self) -> Result<(), Box> { self._track_change(Track::Next) } @@ -700,6 +705,7 @@ impl NetMDInterface { Ok(res[0].to_i64().unwrap() as u8) } + /// The number of tracks on the disc pub fn track_count(&self) -> Result> { self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead); @@ -784,6 +790,7 @@ impl NetMDInterface { Ok(res) } + /// Gets the disc title pub fn disc_title(&self, wchar: bool) -> Result> { let mut title = self._disc_title(wchar)?; @@ -809,6 +816,7 @@ impl NetMDInterface { Ok(title) } + /// Gets all groups on the disc pub fn track_group_list( &self, ) -> Result, Option, Vec)>, Box> { @@ -930,6 +938,7 @@ impl NetMDInterface { .into()) } + // Sets the title of the disc pub fn set_disc_title(&self, title: String, wchar: bool) -> Result<(), Box> { let current_title = self._disc_title(wchar)?; if current_title == title { @@ -981,4 +990,185 @@ impl NetMDInterface { Ok(()) } + + /// Sets the title of a track + pub fn set_track_title(&self, track: u16, title: String, wchar: bool) -> Result<(), Box> { + let new_title: Vec; + let (wchar_value, descriptor) = match wchar { + true => { + new_title = sanitize_full_width_title(&title, false); + (3, Descriptor::AudioUTOC4TD) + }, + false => { + new_title = sanitize_half_width_title(title.clone()); + (2, Descriptor::AudioUTOC1TD) + }, + }; + + let old_len: u16; + let new_len = new_title.len(); + + match self.track_title(track, wchar) { + Ok(current_title) => { + if title == current_title { + return Ok(()) + } + old_len = length_after_encoding_to_jis(¤t_title) as u16; + }, + Err(error) if error.to_string() == "Rejected" => { + old_len = 0; + }, + Err(error) => { + return Err(error); + } + } + + self.change_descriptor_state(&descriptor, &DescriptorAction::OpenWrite); + let mut query = format_query( + "1807 022018%b %w 3000 0a00 5000 %w 0000 %w %*".to_string(), + vec![ + QueryValue::Number(wchar_value), + QueryValue::Number(track as i64), + QueryValue::Number(new_len as i64), + QueryValue::Number(old_len as i64), + QueryValue::Array(new_title), + ], + )?; + let reply = self.send_query(&mut query, false, false)?; + + let _ = scan_query(reply, "1807 022018%? %?%? 3000 0a00 5000 %?%? 0000 %?%?".to_string()); + self.change_descriptor_state(&descriptor, &DescriptorAction::Close); + + Ok(()) + } + + /// Removes a track from the UTOC + pub fn erase_track(&self, track: u16) -> Result<(), Box> { + let mut query = format_query( + "1840 ff01 00 201001 %w".to_string(), + vec![ + QueryValue::Number(track as i64), + ], + )?; + + let _ = self.send_query(&mut query, false, false); + + Ok(()) + } + + /// Moves a track to another position on the disc + pub fn move_track(&self, source: u16, dest: u16) -> Result<(), Box> { + let mut query = format_query( + "1843 ff00 00 201001 %w 201001 %w".to_string(), + vec![ + QueryValue::Number(source as i64), + QueryValue::Number(dest as i64), + ], + )?; + + let _ = self.send_query(&mut query, false, false); + + Ok(()) + } + + fn _get_track_info(&self, track: u16, p1: i32, p2: i32) -> Result, Box> { + self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead); + + let mut query = format_query( + "1806 02201001 %w %w %w ff00 00000000".to_string(), + vec![ + QueryValue::Number(track as i64), + QueryValue::Number(p1 as i64), + QueryValue::Number(p2 as i64), + ], + )?; + + let reply = self.send_query(&mut query, false, false)?; + let res = scan_query(reply, "1806 02201001 %?%? %?%? %?%? 1000 00%?0000 %x".to_string())?; + + self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead); + + return Ok(res[0].to_vec().unwrap()); + } + + /// Gets the length of a track as a `std::time::Duration` + pub fn track_length(&self, track_number: u16) -> Result> { + let raw_value = self._get_track_info(track_number, 0x3000, 0x0100)?; + let result = scan_query(raw_value, "01 0006 0000 %B %B %B %B".to_string())?; + + let hours = result[0].to_i64().unwrap(); + let minutes = result[1].to_i64().unwrap(); + let seconds = result[2].to_i64().unwrap(); + let frames = result[3].to_i64().unwrap(); + + let time_ms = (hours * 3600000000) + (minutes * 60000000) + (seconds * 1000000) + (frames * 11600); + + Ok(std::time::Duration::from_micros(time_ms as u64)) + } + + /// Gets the encoding of a track (SP, LP2, LP4) + pub fn track_encoding(&self, track_number: u16) -> Result> { + let raw_value = self._get_track_info(track_number, 0x3080, 0x0700)?; + let result = scan_query(raw_value, "07 0004 0110 %b %b".to_string())?; + + let final_encoding = match result[0].to_i64() { + Ok(0x90) => Encoding::SP, + Ok(0x92) => Encoding::LP2, + Ok(0x93) => Encoding::LP4, + Ok(e) => return Err(format!("Encoding value {e} out of range (0x90..0x92)").into()), + Err(error) => return Err(error) + }; + + Ok(final_encoding) + } + + /// Gets a track's flags + pub fn track_flags(&self, track: u16) -> Result> { + self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead); + let mut query = format_query( + "1806 01201001 %w ff00 00010008".to_string(), + vec![ + QueryValue::Number(track as i64), + ], + )?; + let reply = self.send_query(&mut query, false, false)?; + + let res = scan_query(reply, "1806 01201001 %?%? 10 00 00010008 %b".to_string())?; + + self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close); + + Ok(res[0].to_i64().unwrap() as u8) + } + + pub fn disc_capacity(&self) -> Result<[std::time::Duration; 3], Box> { + self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead); + let mut query = format_query( + "1806 02101000 3080 0300 ff00 00000000".to_string(), + vec![], + )?; + let reply = self.send_query(&mut query, false, false)?; + let mut result: [std::time::Duration; 3] = [std::time::Duration::from_secs(0); 3]; + + println!("{:?}", reply); + + // 8003 changed to %?03 - Panasonic returns 0803 instead. This byte's meaning is unknown + let res = scan_query( + 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() + )?; //25^ + let res_num: Vec = res.into_iter().map(|v| v.to_i64().unwrap() as u64).collect(); + + println!("{:?}", res_num); + // Create 3 values, `Frames Used`, `Frames Total`, and `Frames Left` + for i in 0..3 { + let tmp = &res_num[(4 * i)..=(4 * i) + 3]; + println!("{:?}", tmp); + let time_micros = (tmp[0] * 3600000000) + (tmp[1] * 60000000) + (tmp[2] * 1000000) + (tmp[3] * 11600); + result[i] = std::time::Duration::from_micros(time_micros); + } + + self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::Close); + + Ok(result) + } } diff --git a/minidisc-rs/src/netmd/query_utils.rs b/minidisc-rs/src/netmd/query_utils.rs index c18095c..0dce538 100644 --- a/minidisc-rs/src/netmd/query_utils.rs +++ b/minidisc-rs/src/netmd/query_utils.rs @@ -25,6 +25,7 @@ const FORMAT_TYPE_LEN_DICT: Lazy> = Lazy::new(|| { const DEBUG: bool = false; +#[derive(Clone, Debug)] pub enum QueryValue { Number(i64), Array(Vec), @@ -250,7 +251,7 @@ pub fn scan_query( u8::from_str_radix(&String::from_iter([half.unwrap(), character]), 16).unwrap(); if format_value != input_value { let i = initial_length - input_stack.len() - 1; - return Err(format!("Format and input mismatch at {i}: expected {format_value:#0x}, got {input_value:#0x} (format {format})").into()); + return Err(format!("Format and input mismatch at {i}: expected {format_value:#04x}, got {input_value:#04x} (format {format})").into()); } half = None; } diff --git a/minidisc-rs/src/netmd/utils.rs b/minidisc-rs/src/netmd/utils.rs index 58db065..f7f46d3 100644 --- a/minidisc-rs/src/netmd/utils.rs +++ b/minidisc-rs/src/netmd/utils.rs @@ -175,3 +175,8 @@ pub fn agressive_sanitize_title(title: &String) -> String { ) .into() } + +pub fn time_to_frames(time: &[i64]) -> i64 { + assert_eq!(time.len(), 4); + return (((time[0] as f64 * 60.0 + time[1] as f64) * 60.0 + time[2] as f64) * 424.0 + time[3] as f64) as i64 +} diff --git a/src/main.rs b/src/main.rs index 52272a2..73c2df0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ fn main() { new_device.read_product_string_ascii(&device_desc) ); + // Ensure the player is a minidisc player and not some other random device let player_controller = match interface::NetMDInterface::new(new_device, device_desc) { Ok(player) => player, Err(_) => continue @@ -30,25 +31,30 @@ fn main() { "Player Model: {}", player_controller.net_md_device.device_name().clone().unwrap() ); - println!("Track Count: {:?}", player_controller.track_count().unwrap()); + println!("Track Count: {:?}", player_controller.track_count().unwrap()); + + println!("TEST CASE: {:?}", player_controller.disc_capacity().unwrap()); - //println!("TEST CASE: {:?}", player_controller); - sleep(std::time::Duration::from_secs(2)); println!( "Disc Title: {: >18} | {}\n-----------------------------------------------------------------", - player_controller.disc_title(false).unwrap().split_at(18).0, + player_controller.disc_title(false).unwrap(), player_controller.disc_title(true).unwrap() ); - let _ = player_controller.play(); - + let mut total = 0; for i in 0..player_controller.track_count().unwrap() { println!( - "Track {: >2}: {: >21} | {}", + "{: >2} | {:0>2}:{:0>2}:{:0>2} | {:?} : {: >21} | {}", i + 1, + (player_controller.track_length(i as u16).unwrap().as_secs() / 60) / 60, + (player_controller.track_length(i as u16).unwrap().as_secs() / 60) % 60, + player_controller.track_length(i as u16).unwrap().as_secs() % 60, + player_controller.track_encoding(i as u16).unwrap(), player_controller.track_title(i as u16, false).unwrap(), player_controller.track_title(i as u16, true).unwrap() ); + total += player_controller.track_length(i as u16).unwrap().as_secs(); } + println!("{}", total); } }