mirror of
https://github.com/G2-Games/minidisc-cli.git
synced 2025-04-19 11:42:53 -05:00
Compare commits
14 commits
Author | SHA1 | Date | |
---|---|---|---|
1bc2f5b18b | |||
e859fd937f | |||
8b193bd16c | |||
4fa7b0ec30 | |||
ac29c6a0a5 | |||
c49a86ed75 | |||
391d0c8599 | |||
885e7508fd | |||
0a75b6a013 | |||
3646a4ac05 | |||
dba4ba0df4 | |||
66863aad1e | |||
2a1d975dce | |||
|
8d4a842a37 |
11 changed files with 562 additions and 220 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "minidisc"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
edition = "2021"
|
||||
homepage = "https://github.com/G2-Games/minidisc-rs/"
|
||||
repository = "https://github.com/G2-Games/minidisc-rs/"
|
||||
|
@ -27,6 +27,9 @@ targets = [
|
|||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4.3"
|
||||
|
||||
[dependencies]
|
||||
diacritics = "0.2.0"
|
||||
encoding_rs = "0.8.33"
|
||||
|
@ -34,7 +37,7 @@ nofmt = "1.0.0"
|
|||
once_cell = "1.18.0"
|
||||
unicode-normalization = "0.1.22"
|
||||
regex = "1.10"
|
||||
cross_usb = "0.3"
|
||||
cross_usb = "0.4"
|
||||
num-derive = "0.4.2"
|
||||
num-traits = "0.2.14"
|
||||
rand = "0.8.5"
|
||||
|
@ -42,11 +45,12 @@ getrandom = { version = "0.2", features = ["js"] }
|
|||
des = "0.8"
|
||||
cbc = "0.1"
|
||||
ecb = "0.1"
|
||||
tokio = { version = "1.36", features = ["sync"] }
|
||||
tokio = { version = "1.36", features = ["full", "sync"] }
|
||||
g2-unicode-jp = "0.4.1"
|
||||
thiserror = "1.0.57"
|
||||
phf = { version = "0.11.2", features = ["phf_macros", "macros"] }
|
||||
byteorder = "1.5.0"
|
||||
log = "0.4.22"
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
gloo = { version = "0.11.0", features = ["futures", "worker"] }
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# Rust Minidisc
|
||||

|
||||

|
||||
[](https://lib.rs/crates/minidisc)
|
||||
[](https://docs.rs/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. Devlopment of this project was made much easier by 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). Go check those projects out!
|
||||
The feature set is very similar to that of [netmd-js](https://github.com/cybercase/netmd-js) which this library is inspired by. Development of this project was made much easier by 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). Go check those projects out!
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Documentation has not been finished and is a work in progress. Any help with it would be appreciated!
|
||||
> Documentation has not been finished and is a work in progress. Any contributions would be appreciated!
|
||||
|
||||
## Current Features
|
||||
### NetMD
|
||||
|
|
32
src/lib.rs
32
src/lib.rs
|
@ -1,4 +1,30 @@
|
|||
/// 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 [netmd::interface::NetMDInterface]
|
||||
//! A crate for controlling NetMD and Hi-MD devices.
|
||||
//!
|
||||
//! This crate is entirely `async` (a necessity because of USB in WASM), but
|
||||
//! it can be used in programs which are not async by using a crate like
|
||||
//! [futures_lite](https://docs.rs/futures-lite/) with the `block_on` function.
|
||||
//!
|
||||
//! To use this library, first you need to get a device from [`cross_usb`] and
|
||||
//! then open a [`netmd::NetMDContext`].
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # tokio_test::block_on(async {
|
||||
//! use cross_usb::get_device;
|
||||
//! use minidisc::netmd::base::DEVICE_IDS_CROSSUSB;
|
||||
//! use minidisc::netmd::NetMDContext;
|
||||
//!
|
||||
//! // Get a device using the built-in list of descriptors for minidisc devices
|
||||
//! let dev_descriptor = cross_usb::get_device(DEVICE_IDS_CROSSUSB.to_vec()).await
|
||||
//! .expect("Failed to find device");
|
||||
//!
|
||||
//! // Open a NetMD Context with the device
|
||||
//! let mut context = NetMDContext::new(dev_descriptor).await
|
||||
//! .expect("Could not create context");
|
||||
//!
|
||||
//! // Perform operations on it ...
|
||||
//! context.list_content().await
|
||||
//! .expect("Could not list disc contents");
|
||||
//! # })
|
||||
//! ```
|
||||
|
||||
pub mod netmd;
|
||||
|
|
42
src/main.rs
Normal file
42
src/main.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use std::{process::exit, time::Duration};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let Ok(player) = cross_usb::get_device(minidisc::netmd::DEVICE_IDS_CROSSUSB.to_vec()).await else {
|
||||
eprintln!("Could not find a MiniDisc device");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
let Ok(mut player) = minidisc::netmd::NetMDContext::new(player).await else {
|
||||
eprintln!("Could not open device!");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
let disc = player.list_content().await.expect("Could not retrieve player's contents");
|
||||
|
||||
for track in disc.tracks() {
|
||||
println!(
|
||||
"{:02}:\n Title: {} | {}\n Duration: {}\n Encoding: {}\n",
|
||||
track.index(),
|
||||
track.title(), track.full_width_title(),
|
||||
pretty_time(track.duration().as_duration()),
|
||||
track.encoding(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_time(dur: Duration) -> String {
|
||||
let mut string = String::new();
|
||||
if dur >= Duration::from_secs(3600) {
|
||||
string.push_str(&format!("{:02}", dur.as_secs() / 3600));
|
||||
string.push(':');
|
||||
}
|
||||
if dur >= Duration::from_secs(60) {
|
||||
string.push_str(&format!("{:02}", (dur.as_secs() / 60) % 3600).to_string());
|
||||
string.push(':');
|
||||
}
|
||||
if dur >= Duration::from_secs(60) {
|
||||
string.push_str(&format!("{:02}", dur.as_secs() % 60).to_string());
|
||||
}
|
||||
string
|
||||
}
|
|
@ -6,8 +6,8 @@ use thiserror::Error;
|
|||
|
||||
// USB stuff
|
||||
use cross_usb::prelude::*;
|
||||
use cross_usb::usb::{ControlIn, ControlOut, ControlType, Recipient, UsbError};
|
||||
use cross_usb::{Descriptor, Interface};
|
||||
use cross_usb::usb::{ControlIn, ControlOut, ControlType, Recipient, Error};
|
||||
use cross_usb::{DeviceInfo, Interface};
|
||||
|
||||
use super::utils::cross_sleep;
|
||||
|
||||
|
@ -15,6 +15,7 @@ const BULK_WRITE_ENDPOINT: u8 = 0x02;
|
|||
const BULK_READ_ENDPOINT: u8 = 0x81;
|
||||
|
||||
nofmt::pls! { // Skip formatting the following info
|
||||
/// Device IDs for use in matching existing devices
|
||||
pub static DEVICE_IDS: &[DeviceId] = &[
|
||||
DeviceId { vendor_id: 0x04dd, product_id: 0x7202, name: Some("Sharp IM-MT899H") },
|
||||
DeviceId { vendor_id: 0x04dd, product_id: 0x9013, name: Some("Sharp IM-DR400") },
|
||||
|
@ -65,6 +66,7 @@ pub static DEVICE_IDS: &[DeviceId] = &[
|
|||
];
|
||||
}
|
||||
|
||||
/// Device IDs for use with [cross_usb]
|
||||
pub static DEVICE_IDS_CROSSUSB: Lazy<Box<[cross_usb::DeviceFilter]>> = Lazy::new(|| {
|
||||
DEVICE_IDS
|
||||
.iter()
|
||||
|
@ -112,10 +114,15 @@ pub enum NetMDError {
|
|||
UnknownDevice(DeviceId),
|
||||
|
||||
#[error("usb connection error")]
|
||||
UsbError(#[from] UsbError),
|
||||
UsbError(#[from] Error),
|
||||
}
|
||||
|
||||
/// A USB connection to a NetMD device
|
||||
/// A low-level USB connection to a NetMD device.
|
||||
///
|
||||
/// With this you can send raw commands to the device and recieve raw data.
|
||||
///
|
||||
/// For simple communication with a NetMD device, you most likely want the
|
||||
/// higher level [`super::NetMDInterface`] or [`super::NetMDContext`] interfaces
|
||||
pub struct NetMD {
|
||||
usb_interface: Interface,
|
||||
model: DeviceId,
|
||||
|
@ -125,7 +132,7 @@ impl NetMD {
|
|||
const READ_REPLY_RETRY_INTERVAL: u32 = 10;
|
||||
|
||||
/// Creates a new interface to a NetMD device
|
||||
pub async fn new(usb_descriptor: Descriptor) -> Result<Self, NetMDError> {
|
||||
pub async fn new(usb_descriptor: DeviceInfo) -> Result<Self, NetMDError> {
|
||||
let mut model = DeviceId {
|
||||
vendor_id: usb_descriptor.vendor_id().await,
|
||||
product_id: usb_descriptor.product_id().await,
|
||||
|
@ -136,7 +143,7 @@ impl NetMD {
|
|||
if device_type.vendor_id == model.vendor_id
|
||||
&& device_type.product_id == model.product_id
|
||||
{
|
||||
model.name = device_type.name.clone();
|
||||
model.name = device_type.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -200,15 +207,17 @@ impl NetMD {
|
|||
Ok((length_bytes, poll_result))
|
||||
}
|
||||
|
||||
/// Send a control message to the device (Raw bytes)
|
||||
pub async fn send_command(&mut self, command: Vec<u8>) -> Result<(), NetMDError> {
|
||||
self._send_command(command, false).await
|
||||
}
|
||||
|
||||
/// Send a factory control message to the device (Raw bytes)
|
||||
pub async fn send_factory_command(&mut self, command: Vec<u8>) -> Result<(), NetMDError> {
|
||||
self._send_command(command, true).await
|
||||
}
|
||||
|
||||
/// Send a control message to the device
|
||||
/// Send a control message to the device, can also send factory commands
|
||||
async fn _send_command(
|
||||
&mut self,
|
||||
command: Vec<u8>,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![cfg_attr(debug_assertions, allow(dead_code))]
|
||||
use cross_usb::Descriptor;
|
||||
use cross_usb::DeviceInfo;
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
use regex::Regex;
|
||||
|
@ -18,6 +18,7 @@ use super::utils::{
|
|||
sanitize_full_width_title, sanitize_half_width_title,
|
||||
};
|
||||
|
||||
/// The current reported status from the device.
|
||||
#[derive(Debug, Clone, Copy, FromPrimitive, PartialEq, Eq)]
|
||||
pub enum OperatingStatus {
|
||||
Ready = 50687,
|
||||
|
@ -31,14 +32,16 @@ pub enum OperatingStatus {
|
|||
ReadyForTransfer = 65319,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A representation of time in the way a NetMD device uses internally.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Time {
|
||||
pub minute: u16,
|
||||
pub second: u16,
|
||||
pub frame: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A representation of the current status of the device.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct DeviceStatus {
|
||||
pub disc_present: bool,
|
||||
pub state: Option<OperatingStatus>,
|
||||
|
@ -46,7 +49,8 @@ pub struct DeviceStatus {
|
|||
pub time: Time,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Information about a single track
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Track {
|
||||
index: u16,
|
||||
title: String,
|
||||
|
@ -58,6 +62,7 @@ pub struct Track {
|
|||
}
|
||||
|
||||
impl Track {
|
||||
/// Get the number of title cells a title will take up.
|
||||
pub fn cells_for_title(&self) -> (usize, usize) {
|
||||
let encoding_name_correction = match self.encoding {
|
||||
Encoding::SP => 0,
|
||||
|
@ -72,8 +77,37 @@ impl Track {
|
|||
usize::max(encoding_name_correction, full_width_length),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u16 {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &String {
|
||||
&self.title
|
||||
}
|
||||
|
||||
pub fn full_width_title(&self) -> &String {
|
||||
&self.full_width_title
|
||||
}
|
||||
|
||||
pub fn duration(&self) -> RawTime {
|
||||
self.duration
|
||||
}
|
||||
|
||||
pub fn channels(&self) -> Channels {
|
||||
self.channel
|
||||
}
|
||||
|
||||
pub fn encoding(&self) -> Encoding {
|
||||
self.encoding
|
||||
}
|
||||
|
||||
pub fn protected(&self) -> TrackFlag {
|
||||
self.protected
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a single group on the disc, containing [`Track`]s
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Group {
|
||||
index: u16,
|
||||
|
@ -82,6 +116,7 @@ pub struct Group {
|
|||
tracks: Vec<Track>,
|
||||
}
|
||||
|
||||
/// Information about a MiniDisc complete with [`Track`]s, [`Group`]s, and metadata.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Disc {
|
||||
title: String,
|
||||
|
@ -260,13 +295,18 @@ impl Disc {
|
|||
}
|
||||
}
|
||||
|
||||
/// Context for interacting with a NetMD device as a wrapper around a [`NetMDInterface`].
|
||||
///
|
||||
/// This struct wraps a [`NetMDInterface`] and allows for some higher level
|
||||
/// functions, but it is still necessary to interact with the [`NetMDInterface`]
|
||||
/// when performing many operations.
|
||||
pub struct NetMDContext {
|
||||
interface: NetMDInterface,
|
||||
}
|
||||
|
||||
impl NetMDContext {
|
||||
/// Create a new context to control a NetMD device
|
||||
pub async fn new(device: Descriptor) -> Result<Self, InterfaceError> {
|
||||
pub async fn new(device: DeviceInfo) -> Result<Self, InterfaceError> {
|
||||
let interface = NetMDInterface::new(device).await?;
|
||||
|
||||
Ok(Self { interface })
|
||||
|
@ -287,6 +327,7 @@ impl NetMDContext {
|
|||
self.interface.track_change(Direction::Restart).await
|
||||
}
|
||||
|
||||
/// Get the current status of the device
|
||||
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?;
|
||||
|
@ -317,6 +358,7 @@ impl NetMDContext {
|
|||
})
|
||||
}
|
||||
|
||||
/// Get a representation of the current disc inserted in the device.
|
||||
pub async fn list_content(&mut self) -> Result<Disc, Box<dyn Error>> {
|
||||
let flags = self.interface.disc_flags().await?;
|
||||
let title = self.interface.disc_title(false).await?;
|
||||
|
@ -392,17 +434,14 @@ impl NetMDContext {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Rename a disc while preserving group titles
|
||||
pub async fn rename_disc(
|
||||
&mut self,
|
||||
new_name: &str,
|
||||
new_fw_name: Option<&str>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let new_name = sanitize_half_width_title(new_name);
|
||||
let new_fw_name = if let Some(name) = new_fw_name {
|
||||
Some(sanitize_full_width_title(name))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let new_fw_name = new_fw_name.map(sanitize_full_width_title);
|
||||
|
||||
let old_name = self.interface.disc_title(false).await?;
|
||||
let old_fw_name = self.interface.disc_title(true).await?;
|
||||
|
@ -475,6 +514,7 @@ impl NetMDContext {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Get a track from the device. This only works with MZ-RH1 devices.
|
||||
pub async fn upload<F: Fn(usize, usize)>(
|
||||
&mut self,
|
||||
track: u16,
|
||||
|
@ -508,7 +548,7 @@ impl NetMDContext {
|
|||
Ok((format, header))
|
||||
}
|
||||
|
||||
pub async fn prepare_download(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
async fn prepare_download(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
while ![OperatingStatus::DiscBlank, OperatingStatus::Ready].contains(
|
||||
&self
|
||||
.device_status()
|
||||
|
@ -528,13 +568,50 @@ impl NetMDContext {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn download<F>(
|
||||
/// Start downloading an [`MDTrack`] to the device.
|
||||
///
|
||||
/// Progress is updated in the `progress_callback` closure.
|
||||
///
|
||||
/// # Downloading a track:
|
||||
/// ```no_run
|
||||
/// # tokio_test::block_on(async {
|
||||
/// use minidisc::netmd::DEVICE_IDS_CROSSUSB;
|
||||
/// use minidisc::netmd::NetMDContext;
|
||||
/// use minidisc::netmd::interface::{MDTrack, NetMDInterface};
|
||||
///
|
||||
/// // Get the minidisc device from cross_usb
|
||||
/// let device = cross_usb::get_device(DEVICE_IDS_CROSSUSB.to_vec()).await.unwrap();
|
||||
///
|
||||
/// // Obtain a NetMDContext and acquire it
|
||||
/// let mut context = NetMDContext::new(device).await.unwrap();
|
||||
/// context.interface_mut().acquire().await.unwrap();
|
||||
///
|
||||
/// // Read in an audio file to a vec, for LP2 and LP4 this must be encoded properly
|
||||
/// let track_contents: Vec<u8> =
|
||||
/// std::fs::read("audio_file.wav")
|
||||
/// .expect("Could not read track")[0x60..].to_vec();
|
||||
///
|
||||
/// // Construct the track
|
||||
/// let track = MDTrack {
|
||||
/// chunk_size: 0x400,
|
||||
/// title: String::from("My Track Title"),
|
||||
/// format: minidisc::netmd::interface::WireFormat::LP2,
|
||||
/// full_width_title: None,
|
||||
/// data: track_contents,
|
||||
/// };
|
||||
///
|
||||
/// // Download it to the player!
|
||||
/// context.download(
|
||||
/// track,
|
||||
/// |out_of: usize, done: usize| println!("Done {} / {}", done, out_of)
|
||||
/// ).await.expect("Starting download failed");
|
||||
/// # })
|
||||
/// ```
|
||||
pub async fn download<F: Fn(usize, usize)>(
|
||||
&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
|
||||
|
@ -549,16 +626,22 @@ impl NetMDContext {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get a reference to the underlying interface.
|
||||
///
|
||||
/// [`NetMDContext::interface_mut()`] is almost certainly more useful
|
||||
/// in most cases.
|
||||
pub fn interface(&self) -> &NetMDInterface {
|
||||
&self.interface
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the underlying interface.
|
||||
pub fn interface_mut(&mut self) -> &mut NetMDInterface {
|
||||
&mut self.interface
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NetMDInterface> for NetMDContext {
|
||||
/// Create a context from an already opened interface.
|
||||
fn from(value: NetMDInterface) -> Self {
|
||||
Self { interface: value }
|
||||
}
|
||||
|
|
|
@ -9,14 +9,95 @@ use super::interface::DataEncryptorInput;
|
|||
type DesEcbEnc = ecb::Decryptor<des::Des>;
|
||||
type DesCbcEnc = cbc::Encryptor<des::Des>;
|
||||
|
||||
pub fn new_thread_encryptor(
|
||||
_input: DataEncryptorInput,
|
||||
) -> UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)> {
|
||||
let (tx, rx) = unbounded_channel::<(Vec<u8>, Vec<u8>, Vec<u8>)>();
|
||||
let input = Box::from(_input);
|
||||
pub struct Encryptor {
|
||||
#[allow(clippy::type_complexity)]
|
||||
channel: Option<UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)>>,
|
||||
state: Option<EncryptorState>,
|
||||
}
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut iv = [0u8; 8];
|
||||
struct EncryptorState {
|
||||
input_data: Vec<u8>,
|
||||
iv: [u8; 8],
|
||||
random_key: [u8; 8],
|
||||
encrypted_random_key: [u8; 8],
|
||||
default_chunk_size: usize,
|
||||
current_chunk_size: usize,
|
||||
offset: usize,
|
||||
packet_count: usize,
|
||||
closed: bool,
|
||||
}
|
||||
|
||||
impl Encryptor {
|
||||
pub fn new_threaded(input: DataEncryptorInput) -> Self {
|
||||
let (tx, rx) = unbounded_channel::<(Vec<u8>, Vec<u8>, Vec<u8>)>();
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut iv = [0u8; 8];
|
||||
|
||||
// Create the random key
|
||||
let mut random_key = [0u8; 8];
|
||||
rand::thread_rng().fill_bytes(&mut random_key);
|
||||
|
||||
// Encrypt it with the kek
|
||||
let mut encrypted_random_key = random_key;
|
||||
if let Err(x) = DesEcbEnc::new(&input.kek.into())
|
||||
.decrypt_padded_mut::<NoPadding>(&mut encrypted_random_key)
|
||||
{
|
||||
panic!("Cannot create main key {:?}", x)
|
||||
};
|
||||
|
||||
let default_chunk_size = match input.chunk_size {
|
||||
0 => 0x00100000,
|
||||
e => e,
|
||||
};
|
||||
|
||||
let mut packet_count = 0u32;
|
||||
let mut current_chunk_size;
|
||||
|
||||
let mut input_data = input.data.clone();
|
||||
if (input_data.len() % input.frame_size) != 0 {
|
||||
let padding_remaining = input.frame_size - (input_data.len() % input.frame_size);
|
||||
input_data.extend(std::iter::repeat(0).take(padding_remaining));
|
||||
}
|
||||
let input_data_length = input_data.len();
|
||||
|
||||
let mut offset: usize = 0;
|
||||
while offset < input_data_length {
|
||||
if packet_count > 0 {
|
||||
current_chunk_size = default_chunk_size;
|
||||
} else {
|
||||
current_chunk_size = default_chunk_size - 24;
|
||||
}
|
||||
|
||||
current_chunk_size = std::cmp::min(current_chunk_size, input_data_length - offset);
|
||||
|
||||
let this_data_chunk = &mut input_data[offset..offset + current_chunk_size];
|
||||
DesCbcEnc::new(&random_key.into(), &iv.into())
|
||||
.encrypt_padded_mut::<NoPadding>(this_data_chunk, current_chunk_size)
|
||||
.unwrap();
|
||||
|
||||
tx.send((
|
||||
encrypted_random_key.to_vec(),
|
||||
iv.to_vec(),
|
||||
this_data_chunk.to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
iv.copy_from_slice(&this_data_chunk[this_data_chunk.len() - 8..]);
|
||||
|
||||
packet_count += 1;
|
||||
offset += current_chunk_size;
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
channel: Some(rx),
|
||||
state: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(input: DataEncryptorInput) -> Self {
|
||||
let iv = [0u8; 8];
|
||||
|
||||
// Create the random key
|
||||
let mut random_key = [0u8; 8];
|
||||
|
@ -35,44 +116,82 @@ pub fn new_thread_encryptor(
|
|||
e => e,
|
||||
};
|
||||
|
||||
let mut packet_count = 0u32;
|
||||
let mut current_chunk_size;
|
||||
let packet_count = 0;
|
||||
let current_chunk_size = 0;
|
||||
|
||||
let mut input_data = input.data.clone();
|
||||
if (input_data.len() % input.frame_size) != 0 {
|
||||
let padding_remaining = input.frame_size - (input_data.len() % input.frame_size);
|
||||
input_data.extend(std::iter::repeat(0).take(padding_remaining));
|
||||
}
|
||||
let input_data_length = input_data.len();
|
||||
|
||||
let mut offset: usize = 0;
|
||||
while offset < input_data_length {
|
||||
if packet_count > 0 {
|
||||
current_chunk_size = default_chunk_size;
|
||||
} else {
|
||||
current_chunk_size = default_chunk_size - 24;
|
||||
let offset: usize = 0;
|
||||
|
||||
Encryptor {
|
||||
channel: None,
|
||||
state: Some(EncryptorState {
|
||||
input_data,
|
||||
iv,
|
||||
random_key,
|
||||
encrypted_random_key,
|
||||
current_chunk_size,
|
||||
offset,
|
||||
default_chunk_size,
|
||||
packet_count,
|
||||
closed: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the next encrypted value
|
||||
pub async fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>, Vec<u8>)> {
|
||||
let output;
|
||||
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
if state.closed {
|
||||
return None
|
||||
}
|
||||
|
||||
current_chunk_size = std::cmp::min(current_chunk_size, input_data_length - offset);
|
||||
if state.packet_count > 0 {
|
||||
state.current_chunk_size = state.default_chunk_size;
|
||||
} else {
|
||||
state.current_chunk_size = state.default_chunk_size - 24;
|
||||
}
|
||||
|
||||
let this_data_chunk = &mut input_data[offset..offset + current_chunk_size];
|
||||
DesCbcEnc::new(&random_key.into(), &iv.into())
|
||||
.encrypt_padded_mut::<NoPadding>(this_data_chunk, current_chunk_size)
|
||||
state.current_chunk_size = std::cmp::min(state.current_chunk_size, state.input_data.len() - state.offset);
|
||||
|
||||
let this_data_chunk = &mut state.input_data[state.offset..state.offset + state.current_chunk_size];
|
||||
DesCbcEnc::new(&state.random_key.into(), &state.iv.into())
|
||||
.encrypt_padded_mut::<NoPadding>(this_data_chunk, state.current_chunk_size)
|
||||
.unwrap();
|
||||
|
||||
tx.send((
|
||||
encrypted_random_key.to_vec(),
|
||||
iv.to_vec(),
|
||||
output = Some((
|
||||
state.encrypted_random_key.to_vec(),
|
||||
state.iv.to_vec(),
|
||||
this_data_chunk.to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
));
|
||||
|
||||
iv.copy_from_slice(&this_data_chunk[this_data_chunk.len() - 8..]);
|
||||
state.iv.copy_from_slice(&this_data_chunk[this_data_chunk.len() - 8..]);
|
||||
|
||||
packet_count += 1;
|
||||
offset += current_chunk_size;
|
||||
state.packet_count += 1;
|
||||
state.offset += state.current_chunk_size;
|
||||
} else if let Some(channel) = self.channel.as_mut() {
|
||||
output = channel.recv().await
|
||||
} else {
|
||||
unreachable!("If you got here, this is bad!");
|
||||
}
|
||||
});
|
||||
|
||||
rx
|
||||
output
|
||||
}
|
||||
|
||||
/// Call close to return none from subsequent calls
|
||||
pub fn close(&mut self) {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.closed = true;
|
||||
} else if let Some(channel) = self.channel.as_mut() {
|
||||
channel.close()
|
||||
} else {
|
||||
unreachable!("If you got here, this is bad!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,19 @@ use crate::netmd::utils::{
|
|||
use cbc::cipher::block_padding::NoPadding;
|
||||
use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit};
|
||||
use encoding_rs::SHIFT_JIS;
|
||||
use log::debug;
|
||||
use num_derive::FromPrimitive;
|
||||
use rand::RngCore;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
|
||||
use super::base::NetMD;
|
||||
use super::encryption::Encryptor;
|
||||
use super::utils::{cross_sleep, to_sjis};
|
||||
|
||||
/// An action to take on the player
|
||||
#[derive(Copy, Clone)]
|
||||
enum Action {
|
||||
Play = 0x75,
|
||||
|
@ -27,12 +29,14 @@ enum Action {
|
|||
Rewind = 0x49,
|
||||
}
|
||||
|
||||
/// Direction of playback, or restart track
|
||||
pub enum Direction {
|
||||
Previous = 0x0002,
|
||||
Next = 0x8001,
|
||||
Restart = 0x0001,
|
||||
}
|
||||
|
||||
/// The format of the disc
|
||||
#[derive(Debug, Clone, Copy, FromPrimitive, PartialEq, Eq)]
|
||||
pub enum DiscFormat {
|
||||
LP4 = 0,
|
||||
|
@ -41,6 +45,7 @@ pub enum DiscFormat {
|
|||
SPStereo = 6,
|
||||
}
|
||||
|
||||
/// The format of the audio
|
||||
#[derive(Clone, Hash, Eq, PartialEq, FromPrimitive)]
|
||||
pub enum WireFormat {
|
||||
Pcm = 0x00,
|
||||
|
@ -69,34 +74,36 @@ impl WireFormat {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// The encoding of the audio
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Encoding {
|
||||
SP = 0x90,
|
||||
LP2 = 0x92,
|
||||
LP4 = 0x93,
|
||||
}
|
||||
|
||||
impl ToString for Encoding {
|
||||
fn to_string(&self) -> String {
|
||||
impl std::fmt::Display for Encoding {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Encoding::SP => String::from("sp"),
|
||||
Encoding::LP2 => String::from("lp2"),
|
||||
Encoding::LP4 => String::from("lp4"),
|
||||
Encoding::SP => write!(f, "sp"),
|
||||
Encoding::LP2 => write!(f, "lp2"),
|
||||
Encoding::LP4 => write!(f, "lp4"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// The number of channels in the audio
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Channels {
|
||||
Mono = 0x01,
|
||||
Stereo = 0x00,
|
||||
}
|
||||
|
||||
impl ToString for Channels {
|
||||
fn to_string(&self) -> String {
|
||||
impl std::fmt::Display for Channels {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Channels::Mono => String::from("mono"),
|
||||
Channels::Stereo => String::from("stereo"),
|
||||
Channels::Mono => write!(f, "mono"),
|
||||
Channels::Stereo => write!(f, "stereo"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,26 +113,29 @@ enum ChannelCount {
|
|||
Stereo = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, FromPrimitive)]
|
||||
/// The protected flag on a track
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
|
||||
pub enum TrackFlag {
|
||||
Protected = 0x03,
|
||||
Unprotected = 0x00,
|
||||
}
|
||||
|
||||
impl ToString for TrackFlag {
|
||||
fn to_string(&self) -> String {
|
||||
impl std::fmt::Display for TrackFlag {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TrackFlag::Protected => String::from("protected"),
|
||||
TrackFlag::Unprotected => String::from("unprotected"),
|
||||
TrackFlag::Protected => write!(f, "protected"),
|
||||
TrackFlag::Unprotected => write!(f, "unprotected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The write protect flag on a disc
|
||||
pub enum DiscFlag {
|
||||
Writable = 0x10,
|
||||
WriteProtected = 0x40,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum NetMDLevel {
|
||||
Level1 = 0x20, // Network MD
|
||||
|
@ -180,6 +190,7 @@ enum DescriptorAction {
|
|||
Close = 0,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(u8)]
|
||||
enum NetmdStatus {
|
||||
// NetMD Protocol return status (first byte of request)
|
||||
|
@ -199,8 +210,8 @@ enum NetmdStatus {
|
|||
}
|
||||
|
||||
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
#[error("invalid status code")]
|
||||
pub struct StatusError;
|
||||
#[error("invalid status code: {}", 1)]
|
||||
pub struct StatusError(u16);
|
||||
|
||||
impl TryFrom<u8> for NetmdStatus {
|
||||
type Error = StatusError;
|
||||
|
@ -219,7 +230,7 @@ impl TryFrom<u8> for NetmdStatus {
|
|||
0x0c => Ok(NetmdStatus::Implemented),
|
||||
0x0d => Ok(NetmdStatus::Changed),
|
||||
0x0f => Ok(NetmdStatus::Interim),
|
||||
_ => Err(StatusError),
|
||||
code => Err(StatusError(code as u16)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,6 +243,7 @@ struct MediaInfo {
|
|||
supports_md_clip: u8,
|
||||
}
|
||||
|
||||
/// An error when encrypting packets
|
||||
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum EncryptionError {
|
||||
#[error("supplied depth of {0} is invalid")]
|
||||
|
@ -241,6 +253,7 @@ pub enum EncryptionError {
|
|||
InvalidLength(&'static str, usize),
|
||||
}
|
||||
|
||||
/// An error for any action in the interface
|
||||
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum InterfaceError {
|
||||
#[error("could not parse data from a device")]
|
||||
|
@ -293,11 +306,14 @@ pub struct NetMDInterface {
|
|||
|
||||
#[allow(dead_code)]
|
||||
impl NetMDInterface {
|
||||
/// The maximum number of times to retry after an interim response
|
||||
const MAX_INTERIM_READ_ATTEMPTS: u8 = 4;
|
||||
|
||||
/// The amount of time to wait after an interim response (in milliseconds)
|
||||
const INTERIM_RESPONSE_RETRY_INTERVAL: u32 = 100;
|
||||
|
||||
/// Get a new interface to a NetMD device
|
||||
pub async fn new(device: cross_usb::Descriptor) -> Result<Self, InterfaceError> {
|
||||
pub async fn new(device: cross_usb::DeviceInfo) -> Result<Self, InterfaceError> {
|
||||
let device = base::NetMD::new(device).await?;
|
||||
Ok(NetMDInterface { device })
|
||||
}
|
||||
|
@ -320,9 +336,9 @@ impl NetMDInterface {
|
|||
)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query("1809 00 ff00 0000 0000".to_string(), vec![])?;
|
||||
let query = format_query("1809 00 ff00 0000 0000".to_string(), vec![])?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(
|
||||
reply,
|
||||
|
@ -430,7 +446,7 @@ impl NetMDInterface {
|
|||
|
||||
query.push(0x00);
|
||||
|
||||
match self.send_query(&mut query, false, false).await {
|
||||
match self.send_query(&query, false, false).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
|
@ -439,7 +455,7 @@ impl NetMDInterface {
|
|||
/// Send a query to the NetMD player
|
||||
async fn send_query(
|
||||
&mut self,
|
||||
query: &mut Vec<u8>,
|
||||
query: &[u8],
|
||||
test: bool,
|
||||
accept_interim: bool,
|
||||
) -> Result<Vec<u8>, InterfaceError> {
|
||||
|
@ -452,7 +468,7 @@ impl NetMDInterface {
|
|||
|
||||
async fn send_command(
|
||||
&mut self,
|
||||
query: &mut Vec<u8>,
|
||||
query: &[u8],
|
||||
test: bool,
|
||||
) -> Result<(), InterfaceError> {
|
||||
let status_byte = match test {
|
||||
|
@ -463,7 +479,7 @@ impl NetMDInterface {
|
|||
let mut new_query = Vec::new();
|
||||
|
||||
new_query.push(status_byte as u8);
|
||||
new_query.append(query);
|
||||
new_query.extend_from_slice(query);
|
||||
|
||||
self.device.send_command(new_query).await?;
|
||||
|
||||
|
@ -478,6 +494,7 @@ impl NetMDInterface {
|
|||
data = self.device.read_reply(None).await?;
|
||||
|
||||
let status = NetmdStatus::try_from(data[0])?;
|
||||
debug!("Device status: {:?}", status);
|
||||
|
||||
match status {
|
||||
NetmdStatus::NotImplemented => {
|
||||
|
@ -510,12 +527,12 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
async fn playback_control(&mut self, action: Action) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"18c3 ff %b 000000".to_string(),
|
||||
vec![QueryValue::Number(action as i64)],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(reply, "18c3 00 %b 000000".to_string())?;
|
||||
|
||||
|
@ -545,9 +562,9 @@ impl NetMDInterface {
|
|||
//TODO: Implement fix for LAM-1
|
||||
/// Stop playback
|
||||
pub async fn stop(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("18c5 ff 00000000".to_string(), vec![])?;
|
||||
let query = format_query("18c5 ff 00000000".to_string(), vec![])?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(reply, "18c5 00 00000000".to_string())?;
|
||||
|
||||
|
@ -555,8 +572,8 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
pub async fn acquire(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("ff 010c ffff ffff ffff ffff ffff ffff".to_string(), vec![])?;
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let query = format_query("ff 010c ffff ffff ffff ffff ffff ffff".to_string(), vec![])?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(reply, "ff 010c ffff ffff ffff ffff ffff ffff".to_string())?;
|
||||
|
||||
|
@ -564,9 +581,9 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
pub async fn release(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("ff 0100 ffff ffff ffff ffff ffff ffff".to_string(), vec![])?;
|
||||
let query = format_query("ff 0100 ffff ffff ffff ffff ffff ffff".to_string(), vec![])?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(reply, "ff 0100 ffff ffff ffff ffff ffff ffff".to_string())?;
|
||||
|
||||
|
@ -580,12 +597,12 @@ impl NetMDInterface {
|
|||
)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1809 8001 0230 8800 0030 8804 00 ff00 00000000".to_string(),
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(
|
||||
reply,
|
||||
|
@ -604,8 +621,6 @@ impl NetMDInterface {
|
|||
pub async fn disc_present(&mut self) -> Result<bool, InterfaceError> {
|
||||
let status = self.status().await?;
|
||||
|
||||
println!("{:X?}", status);
|
||||
|
||||
Ok(status[4] == 0x40)
|
||||
}
|
||||
|
||||
|
@ -617,12 +632,12 @@ impl NetMDInterface {
|
|||
)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1809 8001 0330 8802 0030 8805 0030 8806 00 ff00 00000000".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let result = scan_query(
|
||||
reply,
|
||||
|
@ -635,8 +650,8 @@ impl NetMDInterface {
|
|||
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
|
||||
.await?;
|
||||
|
||||
if operating_status.len() < 2 {
|
||||
return Err(InterfaceError::InvalidStatus(StatusError));
|
||||
if operating_status.len() < 2 && !operating_status.is_empty() {
|
||||
return Err(InterfaceError::InvalidStatus(StatusError(operating_status[0] as u16)));
|
||||
}
|
||||
|
||||
let operating_status_number =
|
||||
|
@ -658,20 +673,23 @@ impl NetMDInterface {
|
|||
)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1809 8001 0330 %w 0030 8805 0030 %w 00 ff00 00000000".to_string(),
|
||||
vec![QueryValue::Number(p1 as i64), QueryValue::Number(p2 as i64)],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(
|
||||
reply,
|
||||
"1809 8001 0330 %?%? %?%? %?%? %?%? %?%? %? 1000 00%?0000 %x %?".to_string(),
|
||||
)?;
|
||||
|
||||
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
|
||||
.await?;
|
||||
self.change_descriptor_state(
|
||||
&Descriptor::OperatingStatusBlock,
|
||||
&DescriptorAction::Close
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(res[0].to_vec().unwrap())
|
||||
}
|
||||
|
@ -689,21 +707,18 @@ impl NetMDInterface {
|
|||
self.change_descriptor_state(
|
||||
&Descriptor::OperatingStatusBlock,
|
||||
&DescriptorAction::OpenRead,
|
||||
)
|
||||
.await?;
|
||||
).await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1809 8001 0430 8802 0030 8805 0030 0003 0030 0002 00 ff00 00000000".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let reply = match self.send_query(&mut query, false, false).await {
|
||||
Ok(result) => result,
|
||||
Err(e) => match e {
|
||||
InterfaceError::Rejected(_) => Vec::new(),
|
||||
_ => return Err(e),
|
||||
},
|
||||
//TODO: Ensure this is ok, we might want to do proper error handling
|
||||
let reply = match self.send_query(&query, false, false).await {
|
||||
Ok(d) => d,
|
||||
Err(_) => return Ok([0u16; 5]),
|
||||
};
|
||||
|
||||
let result = scan_query(reply, "1809 8001 0430 %?%? %?%? %?%? %?%? %?%? %?%? %?%? %? %?00 00%?0000 000b 0002 0007 00 %w %B %B %B %B".to_string())?;
|
||||
|
@ -716,26 +731,28 @@ impl NetMDInterface {
|
|||
result[4].to_i64().unwrap() as u16,
|
||||
];
|
||||
|
||||
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
|
||||
.await?;
|
||||
self.change_descriptor_state(
|
||||
&Descriptor::OperatingStatusBlock,
|
||||
&DescriptorAction::Close
|
||||
).await?;
|
||||
|
||||
Ok(final_result)
|
||||
}
|
||||
|
||||
/// Eject the disc from the player if supported
|
||||
pub async fn eject_disc(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap();
|
||||
let query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap();
|
||||
|
||||
let _reply = self.send_query(&mut query, false, false).await?;
|
||||
let _reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if the machine has the capability to eject a disc
|
||||
pub async fn can_eject_disc(&mut self) -> Result<bool, InterfaceError> {
|
||||
let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap();
|
||||
let query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap();
|
||||
|
||||
match self.send_query(&mut query, true, false).await {
|
||||
match self.send_query(&query, true, false).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
|
@ -743,13 +760,13 @@ impl NetMDInterface {
|
|||
|
||||
/// Move the playback to a specific track
|
||||
pub async fn go_to_track(&mut self, track_number: u16) -> Result<u16, InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1850 ff010000 0000 %w".to_string(),
|
||||
vec![QueryValue::Number(track_number as i64)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(reply, "1850 00010000 0000 %w".to_string())?;
|
||||
|
||||
|
@ -767,7 +784,7 @@ impl NetMDInterface {
|
|||
second: u8,
|
||||
frame: u8,
|
||||
) -> Result<u16, InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1850 ff000000 0000 %w %B%B%B%B".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(track_number as i64),
|
||||
|
@ -779,7 +796,7 @@ impl NetMDInterface {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(reply, "1850 00000000 %?%? %w %B%B%B%B".to_string())?;
|
||||
|
||||
|
@ -788,14 +805,15 @@ impl NetMDInterface {
|
|||
Ok(value as u16)
|
||||
}
|
||||
|
||||
/// Change track in a [`Direction`]
|
||||
pub async fn track_change(&mut self, direction: Direction) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1850 ff10 00000000 %w".to_string(),
|
||||
vec![QueryValue::Number(direction as i64)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(reply, "1850 0010 00000000 %?%?".to_string())?;
|
||||
|
||||
|
@ -804,21 +822,22 @@ impl NetMDInterface {
|
|||
|
||||
/// 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();
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let query = format_query("1840 ff 0000".to_string(), vec![]).unwrap();
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
scan_query(reply, "1840 00 0000".to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Ensure this is returning the correct value, it
|
||||
// looks like it actually might be a 16 bit integer
|
||||
/// Get the flags from a disc
|
||||
pub async fn disc_flags(&mut self) -> Result<u8, InterfaceError> {
|
||||
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query("1806 01101000 ff00 0001000b".to_string(), vec![]).unwrap();
|
||||
let query = format_query("1806 01101000 ff00 0001000b".to_string(), vec![]).unwrap();
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(reply, "1806 01101000 1000 0001000b %b".to_string()).unwrap();
|
||||
|
||||
|
@ -828,15 +847,15 @@ impl NetMDInterface {
|
|||
Ok(res[0].to_i64().unwrap() as u8)
|
||||
}
|
||||
|
||||
/// The number of tracks on the disc
|
||||
/// Get the number of tracks on the disc
|
||||
pub async fn track_count(&mut self) -> Result<u16, InterfaceError> {
|
||||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
|
||||
.await?;
|
||||
|
||||
let mut query =
|
||||
let query =
|
||||
format_query("1806 02101001 3000 1000 ff00 00000000".to_string(), vec![]).unwrap();
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(
|
||||
reply,
|
||||
|
@ -870,7 +889,7 @@ impl NetMDInterface {
|
|||
false => 0,
|
||||
};
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1806 02201801 00%b 3000 0a00 ff00 %w%w".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(wchar_value),
|
||||
|
@ -880,7 +899,7 @@ impl NetMDInterface {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
if remaining == 0 {
|
||||
let res = scan_query(
|
||||
|
@ -917,7 +936,7 @@ impl NetMDInterface {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
/// Gets the disc title
|
||||
/// Gets the disc title without group information
|
||||
pub async fn disc_title(&mut self, wchar: bool) -> Result<String, InterfaceError> {
|
||||
let mut title = self.raw_disc_title(wchar).await?;
|
||||
|
||||
|
@ -1021,11 +1040,13 @@ impl NetMDInterface {
|
|||
));
|
||||
}
|
||||
|
||||
let mut remaining_tracks = Vec::new();
|
||||
for i in 0..track_count {
|
||||
if !track_dict.contains_key(&i) {
|
||||
result.insert(0, (None, None, Vec::from([i])))
|
||||
remaining_tracks.push(i);
|
||||
}
|
||||
}
|
||||
result.insert(0, (None, None, remaining_tracks));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -1051,7 +1072,7 @@ impl NetMDInterface {
|
|||
|
||||
let mut track_titles: Vec<String> = vec![];
|
||||
for i in tracks {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1806 022018%b %w 3000 0a00 ff00 00000000".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(wchar_value),
|
||||
|
@ -1060,7 +1081,7 @@ impl NetMDInterface {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(
|
||||
reply,
|
||||
|
@ -1095,6 +1116,9 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
// Sets the title of the disc
|
||||
//
|
||||
// Caution: This does not respect groups. Use the functions available in
|
||||
// NetMDContext to properly rename a disc.
|
||||
pub async fn set_disc_title(&mut self, title: &str, wchar: bool) -> Result<(), InterfaceError> {
|
||||
let current_title = self.raw_disc_title(wchar).await?;
|
||||
if current_title == title {
|
||||
|
@ -1127,7 +1151,7 @@ impl NetMDInterface {
|
|||
.await?
|
||||
}
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1807 02201801 00%b 3000 0a00 5000 %w 0000 %w %*".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(wchar_value),
|
||||
|
@ -1137,7 +1161,7 @@ impl NetMDInterface {
|
|||
],
|
||||
)?;
|
||||
|
||||
let _ = self.send_query(&mut query, false, false).await;
|
||||
let _ = self.send_query(&query, false, false).await;
|
||||
|
||||
if self.device.vendor_id() == 0x04dd {
|
||||
self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::Close)
|
||||
|
@ -1191,7 +1215,7 @@ impl NetMDInterface {
|
|||
self.change_descriptor_state(&descriptor, &DescriptorAction::OpenWrite)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1807 022018%b %w 3000 0a00 5000 %w 0000 %w %*".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(wchar_value),
|
||||
|
@ -1201,7 +1225,7 @@ impl NetMDInterface {
|
|||
QueryValue::Array(new_title),
|
||||
],
|
||||
)?;
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let _ = scan_query(
|
||||
reply,
|
||||
|
@ -1215,19 +1239,19 @@ impl NetMDInterface {
|
|||
|
||||
/// Erases a track from the disc's UTOC
|
||||
pub async fn erase_track(&mut self, track: u16) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1840 ff01 00 201001 %w".to_string(),
|
||||
vec![QueryValue::Number(track as i64)],
|
||||
)?;
|
||||
|
||||
let _result = self.send_query(&mut query, false, false).await;
|
||||
let _result = self.send_query(&query, false, false).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves a track to another index on the disc
|
||||
pub async fn move_track(&mut self, source: u16, dest: u16) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1843 ff00 00 201001 %w 201001 %w".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(source as i64),
|
||||
|
@ -1235,7 +1259,7 @@ impl NetMDInterface {
|
|||
],
|
||||
)?;
|
||||
|
||||
let _result = self.send_query(&mut query, false, false).await;
|
||||
let _result = self.send_query(&query, false, false).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1250,7 +1274,7 @@ impl NetMDInterface {
|
|||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1806 02201001 %w %w %w ff00 00000000".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(track as i64),
|
||||
|
@ -1259,7 +1283,7 @@ impl NetMDInterface {
|
|||
],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
let res = scan_query(
|
||||
reply,
|
||||
"1806 02201001 %?%? %?%? %?%? 1000 00%?0000 %x".to_string(),
|
||||
|
@ -1282,7 +1306,7 @@ impl NetMDInterface {
|
|||
.await?;
|
||||
|
||||
for track in tracks {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1806 02201001 %w %w %w ff00 00000000".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(track as i64),
|
||||
|
@ -1291,7 +1315,7 @@ impl NetMDInterface {
|
|||
],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(
|
||||
reply,
|
||||
|
@ -1358,11 +1382,11 @@ impl NetMDInterface {
|
|||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let 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).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(reply, "1806 01201001 %?%? 10 00 00010008 %b".to_string())?;
|
||||
|
||||
|
@ -1377,8 +1401,8 @@ impl NetMDInterface {
|
|||
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query("1806 02101000 3080 0300 ff00 00000000".to_string(), vec![])?;
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let query = format_query("1806 02101000 3080 0300 ff00 00000000".to_string(), vec![])?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
// 8003 changed to %?03 - Panasonic returns 0803 instead. This byte's meaning is unknown
|
||||
let res = scan_query(
|
||||
|
@ -1410,12 +1434,12 @@ impl NetMDInterface {
|
|||
)
|
||||
.await?;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1809 8001 0330 8801 0030 8805 0030 8807 00 ff00 00000000".to_string(),
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(reply, "1809 8001 0330 8801 0030 8805 0030 8807 00 1000 000e0000 000c 8805 0008 80e0 0110 %b %b 4000".to_string())?;
|
||||
|
||||
|
@ -1433,12 +1457,12 @@ impl NetMDInterface {
|
|||
track: u16,
|
||||
progress_callback: Option<F>,
|
||||
) -> Result<(DiscFormat, u16, Vec<u8>), InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f003010330 ff00 1001 %w".to_string(),
|
||||
vec![QueryValue::Number((track + 1) as i64)],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, true).await?;
|
||||
let reply = self.send_query(&query, false, true).await?;
|
||||
|
||||
let res = scan_query(
|
||||
reply,
|
||||
|
@ -1473,28 +1497,28 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
pub async fn disable_new_track_protection(&mut self, val: u16) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f0030103 2b ff %w".to_string(),
|
||||
vec![QueryValue::Number(val as i64)],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
scan_query(reply, "1800 080046 f0030103 2b 00 %?%?".to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn enter_secure_session(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("1800 080046 f0030103 80 ff".to_string(), vec![])?;
|
||||
let query = format_query("1800 080046 f0030103 80 ff".to_string(), vec![])?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
scan_query(reply, "1800 080046 f0030103 80 00".to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn leave_secure_session(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("1800 080046 f0030103 81 ff".to_string(), vec![])?;
|
||||
let query = format_query("1800 080046 f0030103 81 ff".to_string(), vec![])?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
scan_query(reply, "1800 080046 f0030103 81 00".to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1506,9 +1530,9 @@ impl NetMDInterface {
|
|||
///
|
||||
/// The leaf ID is a 8-byte constant
|
||||
pub async fn leaf_id(&mut self) -> Result<[u8; 8], InterfaceError> {
|
||||
let mut query = format_query("1800 080046 f0030103 11 ff".to_string(), vec![])?;
|
||||
let 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(&query, false, false).await?;
|
||||
let res = scan_query(reply, "1800 080046 f0030103 11 00 %*".to_string())?;
|
||||
|
||||
Ok(res[0].to_vec().unwrap().try_into().unwrap())
|
||||
|
@ -1530,7 +1554,7 @@ impl NetMDInterface {
|
|||
|
||||
let keychains = keychain.concat();
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f0030103 12 ff %w 0000 %w %d %d %d 00000000 %* %*".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(databytes as i64),
|
||||
|
@ -1543,7 +1567,7 @@ impl NetMDInterface {
|
|||
],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(
|
||||
reply,
|
||||
|
@ -1564,12 +1588,12 @@ impl NetMDInterface {
|
|||
))?;
|
||||
}
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f0030103 20 ff 000000 %*".to_string(),
|
||||
vec![QueryValue::Array(hostnonce)],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(reply, "1800 080046 f0030103 20 %? 000000 %#".to_string())?;
|
||||
|
||||
|
@ -1577,9 +1601,9 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
pub async fn session_key_forget(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("1800 080046 f0030103 21 ff 000000".to_string(), vec![])?;
|
||||
let query = format_query("1800 080046 f0030103 21 ff 000000".to_string(), vec![])?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
let _ = scan_query(reply, "1800 080046 f0030103 21 00 000000".to_string())?;
|
||||
|
||||
Ok(())
|
||||
|
@ -1615,12 +1639,12 @@ impl NetMDInterface {
|
|||
.encrypt_padded_mut::<NoPadding>(message.as_mut_slice(), 32)
|
||||
.unwrap();
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f0030103 22 ff 0000 %*".to_string(),
|
||||
vec![QueryValue::Array(message)],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(reply, "1800 080046 f0030103 22 00 0000".to_string())?;
|
||||
|
||||
|
@ -1644,7 +1668,7 @@ impl NetMDInterface {
|
|||
.encrypt_padded_mut::<NoPadding>(&mut message, 8)
|
||||
.unwrap();
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f0030103 48 ff 00 1001 %w %*".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(track_number as i64),
|
||||
|
@ -1652,7 +1676,7 @@ impl NetMDInterface {
|
|||
],
|
||||
)?;
|
||||
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
scan_query(reply, "1800 080046 f0030103 48 00 00 1001 %?%?".to_string())?;
|
||||
|
||||
|
@ -1660,19 +1684,16 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn send_track<F>(
|
||||
pub async fn send_track<F: Fn(usize, usize)>(
|
||||
&mut self,
|
||||
wireformat: u8,
|
||||
discformat: u8,
|
||||
frames: u32,
|
||||
pkt_size: u32,
|
||||
// key, iv, data
|
||||
mut packets: UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)>,
|
||||
mut packets: Encryptor,
|
||||
hex_session_key: &[u8],
|
||||
progress_callback: F,
|
||||
) -> Result<(u16, Vec<u8>, Vec<u8>), InterfaceError>
|
||||
where
|
||||
F: Fn(usize, usize),
|
||||
{
|
||||
if hex_session_key.len() != 8 {
|
||||
return Err(EncryptionError::InvalidLength(
|
||||
|
@ -1686,7 +1707,7 @@ impl NetMDInterface {
|
|||
|
||||
let total_bytes: usize = (pkt_size + 24) as usize; //framesizedict[wireformat] * frames + pktcount * 24;
|
||||
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f0030103 28 ff 000100 1001 ffff 00 %b %b %d %d".to_string(),
|
||||
vec![
|
||||
QueryValue::Number(wireformat as i64),
|
||||
|
@ -1695,7 +1716,7 @@ impl NetMDInterface {
|
|||
QueryValue::Number(total_bytes as i64),
|
||||
],
|
||||
)?;
|
||||
let mut reply = self.send_query(&mut query, false, true).await?;
|
||||
let mut reply = self.send_query(&query, false, true).await?;
|
||||
scan_query(
|
||||
reply,
|
||||
"1800 080046 f0030103 28 00 000100 1001 %?%? 00 %*".to_string(),
|
||||
|
@ -1705,10 +1726,10 @@ impl NetMDInterface {
|
|||
// Sharps are slow
|
||||
cross_sleep(Duration::from_millis(200)).await;
|
||||
|
||||
let mut _written_bytes = 0;
|
||||
let mut written_bytes = 0;
|
||||
let mut packet_count = 0;
|
||||
|
||||
while let Some((key, iv, data)) = packets.recv().await {
|
||||
while let Some((key, iv, data)) = packets.next().await {
|
||||
let binpack = if packet_count == 0 {
|
||||
let packed_length: Vec<u8> = pkt_size.to_be_bytes().to_vec();
|
||||
[vec![0, 0, 0, 0], packed_length, key, iv, data].concat()
|
||||
|
@ -1716,10 +1737,10 @@ impl NetMDInterface {
|
|||
data
|
||||
};
|
||||
self.device.write_bulk(&binpack).await?;
|
||||
_written_bytes += binpack.len();
|
||||
written_bytes += binpack.len();
|
||||
packet_count += 1;
|
||||
(progress_callback)(total_bytes, _written_bytes);
|
||||
if total_bytes == _written_bytes {
|
||||
(progress_callback)(total_bytes, written_bytes);
|
||||
if total_bytes == written_bytes {
|
||||
packets.close();
|
||||
break;
|
||||
}
|
||||
|
@ -1744,11 +1765,11 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
pub async fn track_uuid(&mut self, track: u16) -> Result<String, InterfaceError> {
|
||||
let mut query = format_query(
|
||||
let query = format_query(
|
||||
"1800 080046 f0030103 23 ff 1001 %w".to_string(),
|
||||
vec![QueryValue::Number(track as i64)],
|
||||
)?;
|
||||
let reply = self.send_query(&mut query, false, false).await?;
|
||||
let reply = self.send_query(&query, false, false).await?;
|
||||
|
||||
let res = scan_query(reply, "1800 080046 f0030103 23 00 1001 %?%? %*".to_string())?;
|
||||
|
||||
|
@ -1756,8 +1777,8 @@ impl NetMDInterface {
|
|||
}
|
||||
|
||||
pub async fn terminate(&mut self) -> Result<(), InterfaceError> {
|
||||
let mut query = format_query("1800 080046 f0030103 2a ff00".to_string(), vec![])?;
|
||||
self.send_query(&mut query, false, false).await?;
|
||||
let query = format_query("1800 080046 f0030103 2a ff00".to_string(), vec![])?;
|
||||
self.send_query(&query, false, false).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1768,7 +1789,7 @@ type DesCbcEnc = cbc::Encryptor<des::Des>;
|
|||
type DesCbcDec = cbc::Decryptor<des::Des>;
|
||||
type TDesCbcEnc = cbc::Encryptor<des::TdesEde3>;
|
||||
|
||||
pub fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec<u8> {
|
||||
pub(super) fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec<u8> {
|
||||
let mut subkey_a = [0u8; 8];
|
||||
subkey_a.copy_from_slice(&key[0..8]);
|
||||
|
||||
|
@ -1794,13 +1815,13 @@ pub fn retailmac(key: &[u8], value: &[u8], iv: &[u8; 8]) -> Vec<u8> {
|
|||
end[..8].to_vec()
|
||||
}
|
||||
|
||||
pub struct EKBData {
|
||||
pub(super) struct EKBData {
|
||||
chains: [[u8; 16]; 2],
|
||||
depth: i32,
|
||||
signature: [u8; 24],
|
||||
}
|
||||
|
||||
pub struct EKBOpenSource;
|
||||
pub(super) struct EKBOpenSource;
|
||||
|
||||
impl EKBOpenSource {
|
||||
pub fn root_key(&self) -> [u8; 16] {
|
||||
|
@ -1841,10 +1862,6 @@ pub struct MDTrack {
|
|||
pub data: Vec<u8>,
|
||||
pub chunk_size: usize,
|
||||
pub full_width_title: Option<String>,
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub encrypt_packets_iterator:
|
||||
Box<dyn Fn(DataEncryptorInput) -> UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)>>,
|
||||
}
|
||||
|
||||
pub struct DataEncryptorInput {
|
||||
|
@ -1899,8 +1916,19 @@ impl MDTrack {
|
|||
[0x14, 0xe3, 0x83, 0x4e, 0xe2, 0xd3, 0xcc, 0xa5]
|
||||
}
|
||||
|
||||
pub fn get_encrypting_iterator(&mut self) -> UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)> {
|
||||
(self.encrypt_packets_iterator)(DataEncryptorInput {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn get_encrypting_iterator(&mut self) -> Encryptor {
|
||||
Encryptor::new_threaded(DataEncryptorInput {
|
||||
kek: self.get_kek(),
|
||||
frame_size: self.frame_size(),
|
||||
chunk_size: self.chunk_size(),
|
||||
data: std::mem::take(&mut self.data),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_family = "wasm")]
|
||||
pub fn get_encrypting_iterator(&mut self) -> Encryptor {
|
||||
Encryptor::new(DataEncryptorInput {
|
||||
kek: self.get_kek(),
|
||||
frame_size: self.frame_size(),
|
||||
chunk_size: self.chunk_size(),
|
||||
|
@ -1909,7 +1937,7 @@ impl MDTrack {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct MDSession<'a> {
|
||||
pub(super) struct MDSession<'a> {
|
||||
pub md: &'a mut NetMDInterface,
|
||||
pub ekb_object: EKBOpenSource,
|
||||
pub hex_session_key: Option<Vec<u8>>,
|
||||
|
@ -1948,14 +1976,12 @@ impl<'a> MDSession<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn download_track<F>(
|
||||
pub async fn download_track<F: Fn(usize, usize)>(
|
||||
&mut self,
|
||||
mut track: MDTrack,
|
||||
progress_callback: F,
|
||||
disc_format: Option<DiscFormat>,
|
||||
) -> Result<(u16, Vec<u8>, Vec<u8>), Box<dyn Error>>
|
||||
where
|
||||
F: Fn(usize, usize),
|
||||
{
|
||||
if self.hex_session_key.is_none() {
|
||||
return Err("Cannot download a track using a non-init()'ed session!".into());
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use phf::phf_map;
|
||||
|
||||
pub static MAPPINGS_JP: phf::Map<&'static str, &'static str> = phf_map![
|
||||
/// Mappings to convert to fullwidth from half width
|
||||
pub static MAPPINGS_JP: phf::Map<&str, &str> = phf_map![
|
||||
"!" =>"!",
|
||||
"\"" =>""",
|
||||
"#" =>"#",
|
||||
|
@ -191,7 +192,9 @@ pub static MAPPINGS_JP: phf::Map<&'static str, &'static str> = phf_map![
|
|||
"。" =>"。",
|
||||
"、" =>"、"
|
||||
];
|
||||
pub static MAPPINGS_RU: phf::Map<&'static str, &'static str> = phf_map![
|
||||
|
||||
/// Mappings to romanize Russian
|
||||
pub static MAPPINGS_RU: phf::Map<&str, &str> = phf_map![
|
||||
"а" =>"a",
|
||||
"б" =>"b",
|
||||
"в" =>"v",
|
||||
|
@ -259,7 +262,9 @@ pub static MAPPINGS_RU: phf::Map<&'static str, &'static str> = phf_map![
|
|||
"Ю" =>"Iu",
|
||||
"Я" =>"Ia"
|
||||
];
|
||||
pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map![
|
||||
|
||||
/// Mappings to remove accents for German
|
||||
pub static MAPPINGS_DE: phf::Map<&str, &str> = phf_map![
|
||||
"Ä" => "Ae",
|
||||
"ä" => "ae",
|
||||
"Ö" => "Oe",
|
||||
|
@ -268,7 +273,9 @@ pub static MAPPINGS_DE: phf::Map<&'static str, &'static str> = phf_map![
|
|||
"ü" => "ue",
|
||||
"ß" => "ss"
|
||||
];
|
||||
pub static MAPPINGS_HW: phf::Map<&'static str, &'static str> = phf_map![
|
||||
|
||||
/// Mappings to make Japanese half width
|
||||
pub static MAPPINGS_HW: phf::Map<&str, &str> = phf_map![
|
||||
"-" =>"-",
|
||||
"ー" =>"-",
|
||||
"ァ" =>"ァ",
|
||||
|
@ -548,7 +555,9 @@ pub static MAPPINGS_HW: phf::Map<&'static str, &'static str> = phf_map![
|
|||
"ゝ" =>"ヽ",
|
||||
"ゞ" =>"ヾ",
|
||||
];
|
||||
pub static ALLOWED_HW_KANA: &[&'static str] = &[
|
||||
|
||||
/// A list of allowed half width kana
|
||||
pub static ALLOWED_HW_KANA: &[&str] = &[
|
||||
"-", "-", "ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ",
|
||||
"ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ", "ダ", "チ",
|
||||
"ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ",
|
||||
|
@ -566,6 +575,8 @@ pub static ALLOWED_HW_KANA: &[&'static str] = &[
|
|||
"マ", "ミ", "ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ヲ", "ン",
|
||||
"ヮ", "ヰ", "ヱ", "ヵ", "ヶ", "ヴ", "ヽ", "ヾ",
|
||||
];
|
||||
|
||||
/// Characters which take up more than one byte
|
||||
pub static MULTI_BYTE_CHARS: phf::Map<char, u8> = phf_map![
|
||||
'ガ' => 1,
|
||||
'ギ' => 1,
|
||||
|
@ -634,6 +645,8 @@ pub static MULTI_BYTE_CHARS: phf::Map<char, u8> = phf_map![
|
|||
'ゝ' => 1,
|
||||
'ゞ' => 1
|
||||
];
|
||||
|
||||
/// Half width to full width conversion for group ranges
|
||||
pub static HW_TO_FW_RANGE_MAP: phf::Map<char, char> = phf_map![
|
||||
'0' => '0',
|
||||
'1' => '1',
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
/*!
|
||||
* This crate is an interface in rust to control NetMD and Hi-MD minidisc devices.
|
||||
*
|
||||
* Documentation coming soon
|
||||
*/
|
||||
//! This module contains all functionality for interacting with NetMD minidisc
|
||||
//! devices.
|
||||
|
||||
pub mod base;
|
||||
pub mod commands;
|
||||
|
@ -11,3 +8,15 @@ pub mod interface;
|
|||
mod mappings;
|
||||
mod query_utils;
|
||||
mod utils;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use base::DEVICE_IDS_CROSSUSB;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use commands::NetMDContext;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use interface::NetMDInterface;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use base::NetMD;
|
||||
|
|
|
@ -90,13 +90,16 @@ pub enum QueryError {
|
|||
#[error("unrecognized format character: `{0}`")]
|
||||
UnrecognizedChar(char),
|
||||
|
||||
#[error("Format and input mismatch at {index}: expected {expected:#04x}, got {actual:#04x} (format {format_string})")]
|
||||
#[error("format and input mismatch at {index}: expected {expected:#04x}, got {actual:#04x} (format {format_string})")]
|
||||
InputMismatch {
|
||||
index: usize,
|
||||
expected: u8,
|
||||
actual: u8,
|
||||
format_string: String,
|
||||
},
|
||||
|
||||
#[error("input data is empty")]
|
||||
EmptyData,
|
||||
}
|
||||
|
||||
/// Formats a query using a standard input to send to the player
|
||||
|
@ -196,6 +199,14 @@ pub fn format_query(format: String, args: Vec<QueryValue>) -> Result<Vec<u8>, Qu
|
|||
|
||||
/// Scans a result using a standard input to recieve from the player
|
||||
pub fn scan_query(query_result: Vec<u8>, format: String) -> Result<Vec<QueryValue>, QueryError> {
|
||||
if DEBUG {
|
||||
println!("RECV<<< F: {}", format);
|
||||
}
|
||||
|
||||
if query_result.is_empty() {
|
||||
return Err(QueryError::EmptyData)
|
||||
}
|
||||
|
||||
let mut result: Vec<QueryValue> = Vec::new();
|
||||
|
||||
let initial_length = query_result.len();
|
||||
|
|
Loading…
Reference in a new issue