Started restructuring commands.rs, updated cross_usb to 0.3.0

This commit is contained in:
G2-Games 2024-03-24 07:08:29 -05:00
parent c292b4b533
commit 4151a4f5c0
5 changed files with 129 additions and 109 deletions

View file

@ -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"

View file

@ -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;

View file

@ -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<Self, NetMDError> {
pub async fn new(usb_descriptor: Descriptor) -> Result<Self, NetMDError> {
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 {

View file

@ -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<DeviceStatus, Box<dyn Error>> {
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<OperatingStatus> = FromPrimitive::from_u16(operating_status);
impl NetMDContext {
/// Create a new context to control a NetMD device
pub async fn new(device: Descriptor) -> Result<Self, InterfaceError> {
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<dyn Error>> {
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<F>(
interface: &mut NetMDInterface,
track: MDTrack,
progress_callback: F,
) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>>
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<DeviceStatus, Box<dyn Error>> {
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<OperatingStatus> = 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<dyn Error>> {
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<F>(
&mut self,
track: MDTrack,
progress_callback: F,
) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>>
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)
}
}

View file

@ -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<Self, InterfaceError> {
let net_md_device = base::NetMD::new(device).await?;
Ok(NetMDInterface { net_md_device })
pub async fn new(device: cross_usb::Descriptor) -> Result<Self, InterfaceError> {
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<u8>, Vec<u8>, Vec<u8>)>,
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<des::TdesEde3>;
pub fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec<u8> {
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::<NoPadding>(&mut beginning, 8)
@ -1729,8 +1715,8 @@ pub fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec<u8> {
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::<NoPadding>(&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,
}
}