mirror of
https://github.com/G2-Games/minidisc-cli.git
synced 2025-04-19 03:32:53 -05:00
Started restructuring commands.rs
, updated cross_usb
to 0.3.0
This commit is contained in:
parent
c292b4b533
commit
4151a4f5c0
5 changed files with 129 additions and 109 deletions
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue