Compare commits

..

No commits in common. "9bdd7ddb393fe10c43057e64af68a117c0640b93" and "2b3712f1db1cf2eceafcf7a1e25eb9b5aeef3248" have entirely different histories.

7 changed files with 46 additions and 94 deletions

View file

@ -1,24 +0,0 @@
name: Rust
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Run clippy
run: cargo clippy --verbose

View file

@ -1,5 +1,5 @@
[package] [package]
name = "minidisc_rs" name = "minidisc_pc"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
homepage = "https://github.com/G2-Games/minidisc-cli/" homepage = "https://github.com/G2-Games/minidisc-cli/"

View file

@ -1,19 +1,3 @@
# Rust Minidisc # minidisc
A library for controlling and interfacing with [MiniDisc](https://en.wikipedia.org/wiki/MiniDisc) devices from within Rust programs. Compatible with many cross platform targets (including Web Assembly!) by using [cross-usb](https://github.com/G2-Games/cross-usb).
The feature set is very similar to that of [netmd-js](https://github.com/cybercase/netmd-js) which this library is inspired by. For more information check out the absolutely awesome [Web Minidisc project](https://github.com/asivery/webminidisc), [NetMD-exploits](https://github.com/asivery/netmd-exploits), and the C based [Linux Minidisc project](https://github.com/linux-minidisc/linux-minidisc). Docs are a work in progress.
Documentation has not been finished and is a work in progress.
## Current Features
### NetMD
- [x] Track upload
- [x] Track management
- [x] Playback control
- [x] Track download ([MZ-RH1](https://www.minidisc.wiki/equipment/sony/portable/mz-rh1) only)
### Hi-MD
- [ ] Track upload
- [ ] Track management
- [ ] Playback control
- [ ] Track download

View file

@ -99,6 +99,7 @@ pub struct DeviceId {
pub struct NetMD { pub struct NetMD {
usb_interface: UsbInterface, usb_interface: UsbInterface,
model: DeviceId, model: DeviceId,
status: Option<Status>,
} }
impl NetMD { impl NetMD {
@ -131,22 +132,23 @@ impl NetMD {
Ok(Self { Ok(Self {
usb_interface, usb_interface,
model, model,
status: None,
}) })
} }
/// Gets the device name, this is limited to the devices in the list /// Gets the device name, this is limited to the devices in the list
pub fn device_name(&self) -> &Option<String> { pub async fn device_name(&self) -> &Option<String> {
&self.model.name &self.model.name
} }
/// Gets the vendor id /// Gets the vendor id
pub fn vendor_id(&self) -> u16 { pub async fn vendor_id(&self) -> &u16 {
self.model.vendor_id &self.model.vendor_id
} }
/// Gets the product id /// Gets the product id
pub fn product_id(&self) -> u16 { pub async fn product_id(&self) -> &u16 {
self.model.product_id &self.model.product_id
} }
/// Poll the device to get either the result /// Poll the device to get either the result

View file

@ -20,16 +20,16 @@ pub enum OperatingStatus {
} }
pub struct Time { pub struct Time {
pub minute: u16, minute: u16,
pub second: u16, second: u16,
pub frame: u16, frame: u16,
} }
pub struct DeviceStatus { pub struct DeviceStatus {
pub disc_present: bool, disc_present: bool,
pub state: Option<OperatingStatus>, state: Option<OperatingStatus>,
pub track: u8, track: u8,
pub time: Time, time: Time,
} }
pub async fn device_status(interface: &mut NetMDInterface) -> Result<DeviceStatus, Box<dyn Error>> { pub async fn device_status(interface: &mut NetMDInterface) -> Result<DeviceStatus, Box<dyn Error>> {

View file

@ -206,7 +206,6 @@ impl NetMDInterface {
const MAX_INTERIM_READ_ATTEMPTS: u8 = 4; const MAX_INTERIM_READ_ATTEMPTS: u8 = 4;
const INTERIM_RESPONSE_RETRY_INTERVAL: u32 = 100; 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, Box<dyn Error>> { pub async fn new(device: &cross_usb::UsbDevice) -> Result<Self, Box<dyn Error>> {
let net_md_device = base::NetMD::new(device).await?; let net_md_device = base::NetMD::new(device).await?;
Ok(NetMDInterface { net_md_device }) Ok(NetMDInterface { net_md_device })
@ -420,7 +419,7 @@ impl NetMDInterface {
async fn playback_control(&mut self, action: Action) -> Result<(), Box<dyn Error>> { async fn playback_control(&mut self, action: Action) -> Result<(), Box<dyn Error>> {
let mut query = format_query( let mut query = format_query(
"18c3 ff %b 000000".to_string(), "18c3 00 %b 000000".to_string(),
vec![QueryValue::Number(action as i64)], vec![QueryValue::Number(action as i64)],
)?; )?;
@ -431,28 +430,23 @@ impl NetMDInterface {
Ok(()) Ok(())
} }
/// Begin playback or resume after paused
pub async fn play(&mut self) -> Result<(), Box<dyn Error>> { pub async fn play(&mut self) -> Result<(), Box<dyn Error>> {
self.playback_control(Action::Play).await self.playback_control(Action::Play).await
} }
/// Fast foward through the disc
pub async fn fast_forward(&mut self) -> Result<(), Box<dyn Error>> { pub async fn fast_forward(&mut self) -> Result<(), Box<dyn Error>> {
self.playback_control(Action::FastForward).await self.playback_control(Action::FastForward).await
} }
/// Rewind through the disc
pub async fn rewind(&mut self) -> Result<(), Box<dyn Error>> { pub async fn rewind(&mut self) -> Result<(), Box<dyn Error>> {
self.playback_control(Action::Rewind).await self.playback_control(Action::Rewind).await
} }
/// Pause playback
pub async fn pause(&mut self) -> Result<(), Box<dyn Error>> { pub async fn pause(&mut self) -> Result<(), Box<dyn Error>> {
self.playback_control(Action::Pause).await self.playback_control(Action::Pause).await
} }
//TODO: Implement fix for LAM-1 //TODO: Implement fix for LAM-1
/// Stop playback
pub async fn stop(&mut self) -> Result<(), Box<dyn Error>> { pub async fn stop(&mut self) -> Result<(), Box<dyn Error>> {
let mut query = format_query("18c5 ff 00000000".to_string(), vec![])?; let mut query = format_query("18c5 ff 00000000".to_string(), vec![])?;
@ -509,7 +503,6 @@ impl NetMDInterface {
Ok(final_array) Ok(final_array)
} }
/// Check if a disc is loaded in the player
pub async fn disc_present(&mut self) -> Result<bool, Box<dyn Error>> { pub async fn disc_present(&mut self) -> Result<bool, Box<dyn Error>> {
let status = self.status().await?; let status = self.status().await?;
@ -568,30 +561,30 @@ impl NetMDInterface {
let mut query = format_query( let mut query = format_query(
"1809 8001 0330 %w 0030 8805 0030 %w 00 ff00 00000000".to_string(), "1809 8001 0330 %w 0030 8805 0030 %w 00 ff00 00000000".to_string(),
vec![QueryValue::Number(p1 as i64), QueryValue::Number(p2 as i64)], vec![QueryValue::Number(p1 as i64), QueryValue::Number(p2 as i64)],
)?; )
.unwrap();
let reply = self.send_query(&mut query, false, false).await?; let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query( let res = scan_query(
reply, reply,
"1809 8001 0330 %?%? %?%? %?%? %?%? %?%? %? 1000 00%?0000 %x %?".to_string(), "1809 8001 0330 %?%? %?%? %?%? %?%? %?%? %? 1000 00%?0000 %x %?".to_string(),
)?; );
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close) self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
.await; .await;
Ok(res[0].to_vec().unwrap()) Ok(res.unwrap()[0].to_vec().unwrap())
} }
pub async fn playback_status1(&mut self) -> Result<Vec<u8>, Box<dyn Error>> { pub async fn playback_status1(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
Ok(self.playback_status_query(0x8801, 0x8807).await?) self.playback_status_query(0x8801, 0x8807).await
} }
pub async fn playback_status2(&mut self) -> Result<Vec<u8>, Box<dyn Error>> { pub async fn playback_status2(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
Ok(self.playback_status_query(0x8802, 0x8806).await?) self.playback_status_query(0x8802, 0x8806).await
} }
/// Get the current playback position
pub async fn position(&mut self) -> Result<[u16; 5], Box<dyn Error>> { pub async fn position(&mut self) -> Result<[u16; 5], Box<dyn Error>> {
self.change_descriptor_state( self.change_descriptor_state(
&Descriptor::OperatingStatusBlock, &Descriptor::OperatingStatusBlock,
@ -627,7 +620,6 @@ impl NetMDInterface {
Ok(final_result) Ok(final_result)
} }
/// Eject the disc from the player if supported
pub async fn eject_disc(&mut self) -> Result<(), Box<dyn Error>> { pub async fn eject_disc(&mut self) -> Result<(), Box<dyn Error>> {
let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap(); let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap();
@ -635,7 +627,6 @@ impl NetMDInterface {
Ok(()) Ok(())
} }
/// Check if the machine has the capability to eject a disc
pub async fn can_eject_disc(&mut self) -> Result<bool, Box<dyn Error>> { pub async fn can_eject_disc(&mut self) -> Result<bool, Box<dyn Error>> {
let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap(); let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap();
@ -645,7 +636,7 @@ impl NetMDInterface {
} }
} }
/// Move the playback to a specific track /* Track control */
pub async fn go_to_track(&mut self, track_number: u16) -> Result<u16, Box<dyn Error>> { pub async fn go_to_track(&mut self, track_number: u16) -> Result<u16, Box<dyn Error>> {
let mut query = format_query( let mut query = format_query(
"1850 ff010000 0000 %w".to_string(), "1850 ff010000 0000 %w".to_string(),
@ -662,7 +653,6 @@ impl NetMDInterface {
Ok(value as u16) Ok(value as u16)
} }
/// Move the playback to a specific time
pub async fn go_to_time( pub async fn go_to_time(
&mut self, &mut self,
track_number: u16, track_number: u16,
@ -713,15 +703,15 @@ impl NetMDInterface {
/// Change to the next track (skip back) /// Change to the next track (skip back)
pub async fn previous_track(&mut self) -> Result<(), Box<dyn Error>> { pub async fn previous_track(&mut self) -> Result<(), Box<dyn Error>> {
self.track_change(Track::Previous).await self.track_change(Track::Next).await
} }
/// Change to the next track (skip to beginning of track) /// Change to the next track (skip to beginning of track)
pub async fn restart_track(&mut self) -> Result<(), Box<dyn Error>> { pub async fn restart_track(&mut self) -> Result<(), Box<dyn Error>> {
self.track_change(Track::Restart).await self.track_change(Track::Next).await
} }
/// Erase the disc entirely /* Content access and control */
pub async fn erase_disc(&mut self) -> Result<(), Box<dyn Error>> { pub async fn erase_disc(&mut self) -> Result<(), Box<dyn Error>> {
let mut query = format_query("1840 ff 0000".to_string(), vec![]).unwrap(); let mut query = format_query("1840 ff 0000".to_string(), vec![]).unwrap();
let reply = self.send_query(&mut query, false, false).await?; let reply = self.send_query(&mut query, false, false).await?;
@ -768,7 +758,6 @@ impl NetMDInterface {
Ok(res[0].to_i64().unwrap() as u16) Ok(res[0].to_i64().unwrap() as u16)
} }
/// Gets the disc title as it is stored
async fn raw_disc_title(&mut self, wchar: bool) -> Result<String, Box<dyn Error>> { async fn raw_disc_title(&mut self, wchar: bool) -> Result<String, Box<dyn Error>> {
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
.await; .await;
@ -1032,7 +1021,7 @@ impl NetMDInterface {
let new_len = new_title.len(); let new_len = new_title.len();
if self.net_md_device.vendor_id() == 0x04dd { if self.net_md_device.vendor_id().await == &0x04dd {
self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::OpenWrite) self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::OpenWrite)
.await .await
} else { } else {
@ -1054,7 +1043,7 @@ impl NetMDInterface {
let _ = self.send_query(&mut query, false, false).await; let _ = self.send_query(&mut query, false, false).await;
if self.net_md_device.vendor_id() == 0x04dd { if self.net_md_device.vendor_id().await == &0x04dd {
self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::Close) self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::Close)
.await .await
} else { } else {
@ -1125,7 +1114,7 @@ impl NetMDInterface {
Ok(()) Ok(())
} }
/// Erases a track from the disc's UTOC /// Removes a track from the UTOC
pub async fn erase_track(&mut self, track: u16) -> Result<(), Box<dyn Error>> { pub async fn erase_track(&mut self, track: u16) -> Result<(), Box<dyn Error>> {
let mut query = format_query( let mut query = format_query(
"1840 ff01 00 201001 %w".to_string(), "1840 ff01 00 201001 %w".to_string(),
@ -1137,7 +1126,7 @@ impl NetMDInterface {
Ok(()) Ok(())
} }
/// Moves a track to another index on the disc /// Moves a track to another position on the disc
pub async fn move_track(&mut self, source: u16, dest: u16) -> Result<(), Box<dyn Error>> { pub async fn move_track(&mut self, source: u16, dest: u16) -> Result<(), Box<dyn Error>> {
let mut query = format_query( let mut query = format_query(
"1843 ff00 00 201001 %w 201001 %w".to_string(), "1843 ff00 00 201001 %w 201001 %w".to_string(),
@ -1152,7 +1141,6 @@ impl NetMDInterface {
Ok(()) Ok(())
} }
/// Raw information about a track
async fn raw_track_info( async fn raw_track_info(
&mut self, &mut self,
track: u16, track: u16,
@ -1183,7 +1171,7 @@ impl NetMDInterface {
Ok(res[0].to_vec().unwrap()) Ok(res[0].to_vec().unwrap())
} }
/// Gets the length of tracks as a [std::time::Duration] from a set /// Gets the length of tracks as a `std::time::Duration` from a set
pub async fn track_lengths( pub async fn track_lengths(
&mut self, &mut self,
tracks: Vec<u16>, tracks: Vec<u16>,
@ -1229,7 +1217,7 @@ impl NetMDInterface {
Ok(times) Ok(times)
} }
/// Gets the length of a track as a [std::time::Duration] /// Gets the length of a track as a `std::time::Duration`
pub async fn track_length( pub async fn track_length(
&mut self, &mut self,
track: u16, track: u16,
@ -1271,7 +1259,7 @@ impl NetMDInterface {
Ok(res[0].to_i64().unwrap() as u8) Ok(res[0].to_i64().unwrap() as u8)
} }
/// Gets the disc capacity as a [std::time::Duration] /// Gets the disc capacity as a `std::time::Duration`
pub async fn disc_capacity(&mut self) -> Result<[std::time::Duration; 3], Box<dyn Error>> { pub async fn disc_capacity(&mut self) -> Result<[std::time::Duration; 3], Box<dyn Error>> {
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead) self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead)
.await; .await;
@ -1394,19 +1382,21 @@ impl NetMDInterface {
Ok(()) Ok(())
} }
/// Read the leaf ID of the present NetMD device. The leaf ID tells /**
/// which keys the device posesses, which is needed to find out which * Read the leaf ID of the present NetMD device. The leaf ID tells
/// parts of the EKB needs to be sent to the device for it to decrypt * which keys the device posesses, which is needed to find out which
/// the root key. * parts of the EKB needs to be sent to the device for it to decrypt
/// * the root key.
/// The leaf ID is a 8-byte constant *
pub async fn leaf_id(&mut self) -> Result<[u8; 8], Box<dyn Error>> { * The leaf ID is a 8-byte constant
**/
pub async fn leaf_id(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
let mut query = format_query("1800 080046 f0030103 11 ff".to_string(), vec![])?; let mut query = format_query("1800 080046 f0030103 11 ff".to_string(), vec![])?;
let reply = self.send_query(&mut query, false, false).await?; let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(reply, "1800 080046 f0030103 11 00 %*".to_string())?; let res = scan_query(reply, "1800 080046 f0030103 11 00 %*".to_string())?;
Ok(res[0].to_vec().unwrap().try_into().unwrap()) Ok(res[0].to_vec().unwrap())
} }
pub async fn send_key_data( pub async fn send_key_data(

View file

@ -4,7 +4,7 @@
* Documentation coming soon * Documentation coming soon
*/ */
pub mod base; mod base;
pub mod commands; pub mod commands;
pub mod encryption; pub mod encryption;
pub mod interface; pub mod interface;