From 4151a4f5c0a5ece99578047914389673afb10244 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Sun, 24 Mar 2024 07:08:29 -0500 Subject: [PATCH] Started restructuring `commands.rs`, updated `cross_usb` to 0.3.0 --- Cargo.toml | 2 +- src/lib.rs | 2 +- src/netmd/base.rs | 14 ++-- src/netmd/commands.rs | 154 +++++++++++++++++++++++++---------------- src/netmd/interface.rs | 66 +++++++----------- 5 files changed, 129 insertions(+), 109 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11a0e11..efd8f2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ once_cell = "1.18.0" unicode-normalization = "0.1.22" regex = "1.10.2" lazy_static = "1.4.0" -cross_usb = "0.2" +cross_usb = "0.3" num-derive = "0.3.3" num-traits = "0.2.14" rand = "0.8.5" diff --git a/src/lib.rs b/src/lib.rs index 0506acc..8318a73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ /// A crate for controlling NetMD and Hi-MD devices. /// -/// To use this library, first you need to get a device from [cross-usb] and then open a [netmd::interface::NetMDInterface] +/// To use this library, first you need to get a device from [cross-usb] and then open [netmd::interface::NetMDInterface] pub mod netmd; diff --git a/src/netmd/base.rs b/src/netmd/base.rs index 9f680d6..c35be95 100644 --- a/src/netmd/base.rs +++ b/src/netmd/base.rs @@ -6,8 +6,9 @@ use once_cell::sync::Lazy; use thiserror::Error; // USB stuff -use cross_usb::usb::{ControlIn, ControlOut, ControlType, Device, Interface, Recipient, UsbError}; -use cross_usb::{UsbDevice, UsbInterface}; +use cross_usb::prelude::*; +use cross_usb::usb::{ControlIn, ControlOut, ControlType, Recipient, UsbError}; +use cross_usb::{Interface, Descriptor}; use super::utils::cross_sleep; @@ -116,7 +117,7 @@ pub enum NetMDError { /// A USB connection to a NetMD device pub struct NetMD { - usb_interface: UsbInterface, + usb_interface: Interface, model: DeviceId, } @@ -124,10 +125,10 @@ impl NetMD { const READ_REPLY_RETRY_INTERVAL: u32 = 10; /// Creates a new interface to a NetMD device - pub async fn new(usb_device: &UsbDevice) -> Result { + pub async fn new(usb_descriptor: Descriptor) -> Result { let mut model = DeviceId { - vendor_id: usb_device.vendor_id().await, - product_id: usb_device.product_id().await, + vendor_id: usb_descriptor.vendor_id().await, + product_id: usb_descriptor.product_id().await, name: None, }; @@ -145,6 +146,7 @@ impl NetMD { Some(_) => (), } + let usb_device = usb_descriptor.open().await?; let usb_interface = usb_device.open_interface(0).await?; Ok(Self { diff --git a/src/netmd/commands.rs b/src/netmd/commands.rs index bb57ea4..367378e 100644 --- a/src/netmd/commands.rs +++ b/src/netmd/commands.rs @@ -3,11 +3,12 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use std::error::Error; use std::time::Duration; +use cross_usb::Descriptor; -use super::interface::{MDSession, MDTrack, NetMDInterface}; +use super::interface::{MDSession, MDTrack, NetMDInterface, Direction, InterfaceError}; use super::utils::cross_sleep; -#[derive(FromPrimitive, PartialEq)] +#[derive(FromPrimitive, PartialEq, Eq)] pub enum OperatingStatus { Ready = 50687, Playing = 50037, @@ -33,71 +34,102 @@ pub struct DeviceStatus { pub time: Time, } -pub async fn device_status(interface: &mut NetMDInterface) -> Result> { - let status = interface.status().await?; - let playback_status = interface.playback_status2().await?; - let b1: u16 = playback_status[4] as u16; - let b2: u16 = playback_status[5] as u16; - let position = interface.position().await?; - let operating_status = b1 << 8 | b2; +pub struct NetMDContext { + interface: NetMDInterface, +} - let track = position[0] as u8; - let disc_present = status[4] != 0x80; - let mut state: Option = FromPrimitive::from_u16(operating_status); +impl NetMDContext { + /// Create a new context to control a NetMD device + pub async fn new(device: Descriptor) -> Result { + let interface = NetMDInterface::new(device).await?; - if state == Some(OperatingStatus::Playing) && !disc_present { - state = Some(OperatingStatus::Ready); + Ok(Self { + interface, + }) } - let time = Time { - minute: position[2], - second: position[3], - frame: position[4], - }; - - Ok(DeviceStatus { - disc_present, - state, - track, - time, - }) -} - -pub async fn prepare_download(interface: &mut NetMDInterface) -> Result<(), Box> { - while ![OperatingStatus::DiscBlank, OperatingStatus::Ready].contains( - &device_status(interface) - .await? - .state - .unwrap_or(OperatingStatus::NoDisc), - ) { - cross_sleep(Duration::from_millis(200)).await; + /// Change to the next track (skip forward) + pub async fn next_track(&mut self) -> Result<(), InterfaceError> { + self.interface.track_change(Direction::Next).await } - let _ = interface.session_key_forget().await; - let _ = interface.leave_secure_session().await; + /// Change to the next track (skip back) + pub async fn previous_track(&mut self) -> Result<(), InterfaceError> { + self.interface.track_change(Direction::Previous).await + } - interface.acquire().await?; - let _ = interface.disable_new_track_protection(1).await; + /// Change to the next track (skip to beginning of track) + pub async fn restart_track(&mut self) -> Result<(), InterfaceError> { + self.interface.track_change(Direction::Restart).await + } - Ok(()) -} - -pub async fn download( - interface: &mut NetMDInterface, - track: MDTrack, - progress_callback: F, -) -> Result<(u16, Vec, Vec), Box> -where - F: Fn(usize, usize), -{ - prepare_download(interface).await?; - let mut session = MDSession::new(interface); - session.init().await?; - let result = session - .download_track(track, progress_callback, None) - .await?; - session.close().await?; - interface.release().await?; - - Ok(result) + pub async fn device_status(&mut self) -> Result> { + let status = self.interface.status().await?; + let playback_status = self.interface.playback_status2().await?; + let b1: u16 = playback_status[4] as u16; + let b2: u16 = playback_status[5] as u16; + let position = self.interface.position().await?; + let operating_status = b1 << 8 | b2; + + let track = position[0] as u8; + let disc_present = status[4] != 0x80; + let mut state: Option = FromPrimitive::from_u16(operating_status); + + if state == Some(OperatingStatus::Playing) && !disc_present { + state = Some(OperatingStatus::Ready); + } + + let time = Time { + minute: position[2], + second: position[3], + frame: position[4], + }; + + Ok(DeviceStatus { + disc_present, + state, + track, + time, + }) + } + + pub async fn prepare_download(&mut self) -> Result<(), Box> { + 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( + &mut self, + track: MDTrack, + progress_callback: F, + ) -> Result<(u16, Vec, Vec), Box> + 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) + } } diff --git a/src/netmd/interface.rs b/src/netmd/interface.rs index d1da7f0..0a333db 100644 --- a/src/netmd/interface.rs +++ b/src/netmd/interface.rs @@ -29,7 +29,7 @@ enum Action { Rewind = 0x49, } -enum Track { +pub enum Direction { Previous = 0x0002, Next = 0x8001, Restart = 0x0001, @@ -260,7 +260,7 @@ pub enum InterfaceError { /// An interface for interacting with a NetMD device pub struct NetMDInterface { - pub net_md_device: NetMD, + pub device: NetMD, } #[allow(dead_code)] @@ -269,9 +269,9 @@ impl NetMDInterface { const INTERIM_RESPONSE_RETRY_INTERVAL: u32 = 100; /// Get a new interface to a NetMD device - pub async fn new(device: &cross_usb::UsbDevice) -> Result { - let net_md_device = base::NetMD::new(device).await?; - Ok(NetMDInterface { net_md_device }) + pub async fn new(device: cross_usb::Descriptor) -> Result { + let device = base::NetMD::new(device).await?; + Ok(NetMDInterface { device }) } fn construct_multibyte(&mut self, buffer: &[u8], n: u8, offset: &mut usize) -> u32 { @@ -432,7 +432,7 @@ impl NetMDInterface { new_query.push(status_byte as u8); new_query.append(query); - self.net_md_device.send_command(new_query).await?; + self.device.send_command(new_query).await?; Ok(()) } @@ -442,15 +442,15 @@ impl NetMDInterface { let mut data; while current_attempt < Self::MAX_INTERIM_READ_ATTEMPTS { - data = self.net_md_device.read_reply(None).await?; + data = self.device.read_reply(None).await?; let status = NetmdStatus::try_from(data[0])?; match status { NetmdStatus::NotImplemented => { - return Err(InterfaceError::NotImplemented(format!("{:X?}", data))) + return Err(InterfaceError::NotImplemented(format!("{:02X?}", data))) } - NetmdStatus::Rejected => return Err(InterfaceError::Rejected(format!("{:X?}", data))), + NetmdStatus::Rejected => return Err(InterfaceError::Rejected(format!("{:02X?}", data))), NetmdStatus::Interim if !accept_interim => { let sleep_time = Self::INTERIM_RESPONSE_RETRY_INTERVAL * (u32::pow(2, current_attempt as u32) - 1); @@ -466,7 +466,7 @@ impl NetMDInterface { } return Ok(data); } - _ => return Err(InterfaceError::Unknown(format!("{:X?}", data))), + _ => return Err(InterfaceError::Unknown(format!("{:02X?}", data))), } } @@ -693,6 +693,7 @@ impl NetMDInterface { let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap(); let _reply = self.send_query(&mut query, false, false).await?; + Ok(()) } @@ -753,7 +754,7 @@ impl NetMDInterface { Ok(value as u16) } - async fn track_change(&mut self, direction: Track) -> Result<(), InterfaceError> { + pub async fn track_change(&mut self, direction: Direction) -> Result<(), InterfaceError> { let mut query = format_query( "1850 ff10 00000000 %w".to_string(), vec![QueryValue::Number(direction as i64)], @@ -767,21 +768,6 @@ impl NetMDInterface { Ok(()) } - /// Change to the next track (skip forward) - pub async fn next_track(&mut self) -> Result<(), InterfaceError> { - self.track_change(Track::Next).await - } - - /// Change to the next track (skip back) - pub async fn previous_track(&mut self) -> Result<(), InterfaceError> { - self.track_change(Track::Previous).await - } - - /// Change to the next track (skip to beginning of track) - pub async fn restart_track(&mut self) -> Result<(), InterfaceError> { - self.track_change(Track::Restart).await - } - /// Erase the disc entirely pub async fn erase_disc(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("1840 ff 0000".to_string(), vec![]).unwrap(); @@ -1094,7 +1080,7 @@ impl NetMDInterface { let new_len = new_title.len(); - if self.net_md_device.vendor_id() == 0x04dd { + if self.device.vendor_id() == 0x04dd { self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::OpenWrite) .await? } else { @@ -1116,7 +1102,7 @@ impl NetMDInterface { let _ = self.send_query(&mut query, false, false).await; - if self.net_md_device.vendor_id() == 0x04dd { + if self.device.vendor_id() == 0x04dd { self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::Close) .await? } else { @@ -1414,7 +1400,7 @@ impl NetMDInterface { let codec = res[1].to_i64().unwrap() as u8; let length = res[2].to_i64().unwrap() as usize; - let result = self.net_md_device.read_bulk(length, 0x10000).await?; + let result = self.device.read_bulk(length, 0x10000).await?; scan_query( self.read_reply(false).await?, @@ -1613,7 +1599,7 @@ impl NetMDInterface { discformat: u8, frames: u32, pkt_size: u32, - // key // iv // data + // key, iv, data mut packets: UnboundedReceiver<(Vec, Vec, Vec)>, hex_session_key: &[u8], progress_callback: F, @@ -1644,7 +1630,7 @@ impl NetMDInterface { reply, "1800 080046 f0030103 28 00 000100 1001 %?%? 00 %*".to_string(), )?; - self.net_md_device.poll().await?; + self.device.poll().await?; // Sharps are slow cross_sleep(Duration::from_millis(200)).await; @@ -1659,7 +1645,7 @@ impl NetMDInterface { } else { data }; - self.net_md_device.write_bulk(&binpack).await?; + self.device.write_bulk(&binpack).await?; _written_bytes += binpack.len(); packet_count += 1; (progress_callback)(total_bytes, _written_bytes); @@ -1670,7 +1656,7 @@ impl NetMDInterface { } reply = self.read_reply(false).await?; - self.net_md_device.poll().await?; + self.device.poll().await?; let res = scan_query( reply, "1800 080046 f0030103 28 00 000100 1001 %w 00 %?%? %?%?%?%? %?%?%?%? %*".to_string(), @@ -1714,13 +1700,13 @@ type TDesCbcEnc = cbc::Encryptor; pub fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec { let mut subkey_a = [0u8; 8]; - subkey_a.clone_from_slice(&key[0..8]); + subkey_a.copy_from_slice(&key[0..8]); let mut beginning = [0u8; 8]; - beginning.clone_from_slice(&value[0..8]); + beginning.copy_from_slice(&value[0..8]); let mut end = [0u8; 8]; - end.clone_from_slice(&value[8..]); + end.copy_from_slice(&value[8..]); DesCbcEnc::new(&subkey_a.into(), iv.into()) .encrypt_padded_mut::(&mut beginning, 8) @@ -1729,8 +1715,8 @@ pub fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec { let iv2 = &beginning[beginning.len() - 8..]; let mut wonky_key = [0u8; 24]; - wonky_key[0..16].clone_from_slice(key); - wonky_key[16..].clone_from_slice(&key[0..8]); + wonky_key[0..16].copy_from_slice(key); + wonky_key[16..].copy_from_slice(&key[0..8]); TDesCbcEnc::new(&wonky_key.into(), iv2.into()) .encrypt_padded_mut::(&mut end, 8) .unwrap(); @@ -1753,7 +1739,7 @@ pub struct EKBData { signature: [u8; 24], } -pub struct EKBOpenSource {} +pub struct EKBOpenSource; impl EKBOpenSource { pub fn root_key(&self) -> [u8; 16] { @@ -1954,7 +1940,7 @@ impl<'a> MDSession<'a> { pub fn new(md: &'a mut NetMDInterface) -> Self { MDSession { md, - ekb_object: EKBOpenSource {}, + ekb_object: EKBOpenSource, hex_session_key: None, } }