minidisc-cli/src/netmd/interface.rs

1961 lines
62 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#![cfg_attr(debug_assertions, allow(dead_code))]
use crate::netmd::base;
use crate::netmd::query_utils::{format_query, scan_query, QueryValue};
use crate::netmd::utils::{
half_width_to_full_width_range, length_after_encoding_to_jis, sanitize_full_width_title,
sanitize_half_width_title, time_to_duration,
};
use cbc::cipher::block_padding::NoPadding;
use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit};
use encoding_rs::SHIFT_JIS;
use num_derive::FromPrimitive;
use rand::RngCore;
use std::collections::HashMap;
use std::time::Duration;
use thiserror::Error;
use std::error::Error;
use tokio::sync::mpsc::UnboundedReceiver;
use lazy_static::lazy_static;
use super::base::NetMD;
use super::utils::cross_sleep;
#[derive(Copy, Clone)]
enum Action {
Play = 0x75,
Pause = 0x7d,
FastForward = 0x39,
Rewind = 0x49,
}
enum Track {
Previous = 0x0002,
Next = 0x8001,
Restart = 0x0001,
}
#[derive(Debug, Clone, Copy, FromPrimitive)]
pub enum DiscFormat {
LP4 = 0,
LP2 = 2,
SPMono = 4,
SPStereo = 6,
}
#[derive(Clone, Hash, Eq, PartialEq, FromPrimitive)]
pub enum WireFormat {
Pcm = 0x00,
L105kbps = 0x90,
LP2 = 0x94,
LP4 = 0xA8,
}
impl WireFormat {
fn frame_size(&self) -> u16 {
match self {
WireFormat::Pcm => 2048,
WireFormat::L105kbps => 192,
WireFormat::LP2 => 152,
WireFormat::LP4 => 96,
}
}
}
#[derive(Debug)]
pub enum Encoding {
SP = 0x90,
LP2 = 0x92,
LP4 = 0x93,
}
enum Channels {
Mono = 0x01,
Stereo = 0x00,
}
enum ChannelCount {
Mono = 1,
Stereo = 2,
}
enum TrackFlag {
Protected = 0x03,
Unprotected = 0x00,
}
enum DiscFlag {
Writable = 0x10,
WriteProtected = 0x40,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum NetMDLevel {
Level1 = 0x20, // Network MD
Level2 = 0x50, // Program play MD
Level3 = 0x70, // Editing MD
}
impl std::convert::TryFrom<u8> for NetMDLevel {
type Error = InterfaceError;
fn try_from(item: u8) -> Result<Self, InterfaceError> {
match item {
0x20 => Ok(NetMDLevel::Level1),
0x50 => Ok(NetMDLevel::Level2),
0x70 => Ok(NetMDLevel::Level3),
_ => Err(InterfaceError::InvalidLevel(item)),
}
}
}
enum Descriptor {
DiscTitleTD,
AudioUTOC1TD,
AudioUTOC4TD,
Dstid,
AudioContentsTD,
RootTD,
DiscSubunitIdentifier,
OperatingStatusBlock,
}
impl Descriptor {
fn get_array(&self) -> Vec<u8> {
match self {
Descriptor::DiscTitleTD => vec![0x10, 0x18, 0x01],
Descriptor::AudioUTOC1TD => vec![0x10, 0x18, 0x02],
Descriptor::AudioUTOC4TD => vec![0x10, 0x18, 0x03],
Descriptor::Dstid => vec![0x10, 0x18, 0x04],
Descriptor::AudioContentsTD => vec![0x10, 0x10, 0x01],
Descriptor::RootTD => vec![0x10, 0x10, 0x00],
Descriptor::DiscSubunitIdentifier => vec![0x00],
Descriptor::OperatingStatusBlock => vec![0x80, 0x00],
}
}
}
#[derive(Copy, Clone)]
enum DescriptorAction {
OpenRead = 1,
OpenWrite = 3,
Close = 0,
}
#[repr(u8)]
enum NetmdStatus {
// NetMD Protocol return status (first byte of request)
Control = 0x00,
Status = 0x01,
SpecificInquiry = 0x02,
Notify = 0x03,
GeneralInquiry = 0x04,
// ... (first byte of response)
NotImplemented = 0x08,
Accepted = 0x09,
Rejected = 0x0a,
InTransition = 0x0b,
Implemented = 0x0c,
Changed = 0x0d,
Interim = 0x0f,
}
lazy_static! {
static ref FRAME_SIZE: HashMap<WireFormat, usize> = HashMap::from([
(WireFormat::Pcm, 2048),
(WireFormat::LP2, 192),
(WireFormat::L105kbps, 152),
(WireFormat::LP4, 96),
]);
}
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[error("invalid status code")]
pub struct StatusError;
impl TryFrom<u8> for NetmdStatus {
type Error = StatusError;
fn try_from(item: u8) -> Result<Self, StatusError> {
match item {
0x00 => Ok(NetmdStatus::Control),
0x01 => Ok(NetmdStatus::Status),
0x02 => Ok(NetmdStatus::SpecificInquiry),
0x03 => Ok(NetmdStatus::Notify),
0x04 => Ok(NetmdStatus::GeneralInquiry),
0x08 => Ok(NetmdStatus::NotImplemented),
0x09 => Ok(NetmdStatus::Accepted),
0x0a => Ok(NetmdStatus::Rejected),
0x0b => Ok(NetmdStatus::InTransition),
0x0c => Ok(NetmdStatus::Implemented),
0x0d => Ok(NetmdStatus::Changed),
0x0f => Ok(NetmdStatus::Interim),
_ => Err(StatusError),
}
}
}
struct MediaInfo {
supported_media_type: u32,
implementation_profile_id: u8,
media_type_attributes: u8,
md_audio_version: u8,
supports_md_clip: u8,
}
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum EncryptionError {
#[error("supplied depth of {0} is invalid")]
InvalidDepth(i32),
#[error("supplied {0} length of {1} is invalid")]
InvalidLength(&'static str, usize),
}
#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum InterfaceError {
#[error("could not parse data from a device")]
QueryError(#[from] crate::netmd::query_utils::QueryError),
#[error("communication with the device failed")]
CommunicationError(#[from] crate::netmd::base::NetMDError),
#[error("track {0} is in 2 groups")]
GroupError(String),
#[error("encryption error: {0:?}")]
EncryptionError(#[from] EncryptionError),
#[error("device status invalid: {0:?}")]
InvalidStatus(#[from] StatusError),
#[error("no supported media types found")]
NoSupportedMedia,
#[error("invalid NetMD level: {0}")]
InvalidLevel(u8),
#[error("track encoding value {0} out of range (0x90..0x92)")]
InvalidEncoding(u8),
#[error("disc format value {0} out of range (0..6)")]
InvalidDiscFormat(u8),
#[error("the device rejected the message")]
Rejected(String),
#[error("the title is the same as the target title")]
TitleError,
#[error("the device replied that the command was not recognized")]
NotImplemented(String),
#[error("the maximum number of retry attempts was reached")]
MaxRetries,
#[error("the device replied with an unknown status")]
Unknown(String),
}
/// An interface for interacting with a NetMD device
pub struct NetMDInterface {
pub net_md_device: NetMD,
}
#[allow(dead_code)]
impl NetMDInterface {
const MAX_INTERIM_READ_ATTEMPTS: u8 = 4;
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 })
}
fn construct_multibyte(&mut self, buffer: &[u8], n: u8, offset: &mut usize) -> u32 {
let mut output: u32 = 0;
for _ in 0..n as usize {
output <<= 8;
output |= buffer[*offset] as u32;
*offset += 1;
}
output
}
// TODO: Finish proper implementation
async fn disc_subunit_identifier(&mut self) -> Result<NetMDLevel, InterfaceError> {
self.change_descriptor_state(
&Descriptor::DiscSubunitIdentifier,
&DescriptorAction::OpenRead,
).await?;
let mut query = format_query("1809 00 ff00 0000 0000".to_string(), vec![])?;
let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(
reply,
"1809 00 1000 %?%? %?%? %w %b %b %b %b %w %*".to_string(),
)?;
let _descriptor_length = res[0].to_i64().unwrap();
let _generation_id = res[1].to_i64().unwrap();
let size_of_list_id = res[2].to_i64().unwrap();
let _size_of_object_id = res[3].to_i64().unwrap();
let _size_of_object_position = res[4].to_i64().unwrap();
let amt_of_root_object_lists = res[5].to_i64().unwrap();
let buffer = res[6].to_vec().unwrap();
let mut root_objects: Vec<u32> = Vec::new();
let mut buffer_offset: usize = 0;
for _ in 0..amt_of_root_object_lists {
root_objects.push(self.construct_multibyte(
&buffer,
size_of_list_id as u8,
&mut buffer_offset,
));
}
let _subunit_dependent_length = self.construct_multibyte(&buffer, 2, &mut buffer_offset);
let _subunit_fields_length = self.construct_multibyte(&buffer, 2, &mut buffer_offset);
let _attributes = buffer[buffer_offset];
buffer_offset += 1;
let _disc_subunit_version = buffer[buffer_offset];
buffer_offset += 1;
let mut supported_media_type_specifications: Vec<MediaInfo> = Vec::new();
let amt_supported_media_types = buffer[buffer_offset];
buffer_offset += 1;
for _ in 0..amt_supported_media_types {
let supported_media_type = self.construct_multibyte(&buffer, 2, &mut buffer_offset);
let implementation_profile_id = buffer[buffer_offset];
buffer_offset += 1;
let media_type_attributes = buffer[buffer_offset];
buffer_offset += 1;
let _type_dep_length = self.construct_multibyte(&buffer, 2, &mut buffer_offset);
let md_audio_version = buffer[buffer_offset];
buffer_offset += 1;
let supports_md_clip = buffer[buffer_offset];
buffer_offset += 1;
supported_media_type_specifications.push(MediaInfo {
supported_media_type,
implementation_profile_id,
media_type_attributes,
md_audio_version,
supports_md_clip,
})
}
let manufacturer_dep_length = self.construct_multibyte(&buffer, 2, &mut buffer_offset);
let _manufacturer_dep_data =
&buffer[buffer_offset..buffer_offset + manufacturer_dep_length as usize];
self.change_descriptor_state(&Descriptor::DiscSubunitIdentifier, &DescriptorAction::Close)
.await?;
for media in supported_media_type_specifications {
if media.supported_media_type != 0x301 {
continue;
}
return NetMDLevel::try_from(media.implementation_profile_id);
}
Err(InterfaceError::NoSupportedMedia)
}
/* TODO: Finish implementation
fn factory(&mut self) -> Result<NetMDLevel, Box<dyn Error>> {
let device_name = self.net_md_device.device_name().expect("The device has no name");
let himd = device_name.contains("MZ-RH") || device_name.contains("MZ-NH");
self.disc_subunit_identifier()?;
let constructor =
}
*/
async fn net_md_level(&mut self) -> Result<NetMDLevel, InterfaceError> {
let result = self.disc_subunit_identifier().await?;
Ok(result)
}
async fn change_descriptor_state(
&mut self,
descriptor: &Descriptor,
action: &DescriptorAction,
) -> Result<(), InterfaceError> {
let mut query = format_query("1808".to_string(), vec![]).unwrap();
query.append(&mut descriptor.get_array());
query.push(*action as u8);
query.push(0x00);
match self.send_query(&mut query, false, false).await {
Ok(_) => Ok(()),
Err(err) => Err(err),
}
}
/// Send a query to the NetMD player
async fn send_query(
&mut self,
query: &mut Vec<u8>,
test: bool,
accept_interim: bool,
) -> Result<Vec<u8>, InterfaceError> {
self.send_command(query, test).await?;
let result = self.read_reply(accept_interim).await?;
Ok(result)
}
async fn send_command(&mut self, query: &mut Vec<u8>, test: bool) -> Result<(), InterfaceError> {
let status_byte = match test {
true => NetmdStatus::GeneralInquiry,
false => NetmdStatus::Control,
};
let mut new_query = Vec::new();
new_query.push(status_byte as u8);
new_query.append(query);
self.net_md_device.send_command(new_query).await?;
Ok(())
}
async fn read_reply(&mut self, accept_interim: bool) -> Result<Vec<u8>, InterfaceError> {
let mut current_attempt = 0;
let mut data;
while current_attempt < Self::MAX_INTERIM_READ_ATTEMPTS {
data = self.net_md_device.read_reply(None).await?;
let status = NetmdStatus::try_from(data[0])?;
match status {
NetmdStatus::NotImplemented => {
return Err(InterfaceError::NotImplemented(format!("{:X?}", data)))
}
NetmdStatus::Rejected => return Err(InterfaceError::Rejected(format!("{:X?}", data))),
NetmdStatus::Interim if !accept_interim => {
let sleep_time = Self::INTERIM_RESPONSE_RETRY_INTERVAL
* (u32::pow(2, current_attempt as u32) - 1);
cross_sleep(Duration::from_millis(sleep_time as u64)).await;
current_attempt += 1;
continue; // Retry!
}
NetmdStatus::Accepted | NetmdStatus::Implemented | NetmdStatus::Interim => {
if current_attempt >= Self::MAX_INTERIM_READ_ATTEMPTS {
return Err(InterfaceError::MaxRetries);
}
return Ok(data);
}
_ => return Err(InterfaceError::Unknown(format!("{:X?}", data))),
}
}
// This should NEVER happen unless the code is changed wrongly
unreachable!("The max number of retries is set to 0!")
}
async fn playback_control(&mut self, action: Action) -> Result<(), InterfaceError> {
let mut 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?;
scan_query(reply, "18c3 00 %b 000000".to_string())?;
Ok(())
}
/// Begin playback or resume after paused
pub async fn play(&mut self) -> Result<(), InterfaceError> {
self.playback_control(Action::Play).await
}
/// Fast foward through the disc
pub async fn fast_forward(&mut self) -> Result<(), InterfaceError> {
self.playback_control(Action::FastForward).await
}
/// Rewind through the disc
pub async fn rewind(&mut self) -> Result<(), InterfaceError> {
self.playback_control(Action::Rewind).await
}
/// Pause playback
pub async fn pause(&mut self) -> Result<(), InterfaceError> {
self.playback_control(Action::Pause).await
}
//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 reply = self.send_query(&mut query, false, false).await?;
scan_query(reply, "18c5 00 00000000".to_string())?;
Ok(())
}
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?;
scan_query(reply, "ff 010c ffff ffff ffff ffff ffff ffff".to_string())?;
Ok(())
}
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 reply = self.send_query(&mut query, false, false).await?;
scan_query(reply, "ff 0100 ffff ffff ffff ffff ffff ffff".to_string())?;
Ok(())
}
pub async fn status(&mut self) -> Result<Vec<u8>, InterfaceError> {
self.change_descriptor_state(
&Descriptor::OperatingStatusBlock,
&DescriptorAction::OpenRead,
).await?;
let mut 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 res = scan_query(
reply,
"1809 8001 0230 8800 0030 8804 00 1000 00090000 %x".to_string(),
)?;
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
.await?;
let final_array = res[0].to_vec().unwrap();
Ok(final_array)
}
/// Check if a disc is loaded in the player
pub async fn disc_present(&mut self) -> Result<bool, InterfaceError> {
let status = self.status().await?;
println!("{:X?}", status);
Ok(status[4] == 0x40)
}
async fn full_operating_status(&mut self) -> Result<(u8, u16), InterfaceError> {
// WARNING: Does not work for all devices. See https://github.com/cybercase/webminidisc/issues/21
self.change_descriptor_state(
&Descriptor::OperatingStatusBlock,
&DescriptorAction::OpenRead,
)
.await?;
let mut 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 result = scan_query(
reply,
"1809 8001 0330 8802 0030 8805 0030 8806 00 1000 00%?0000 00%b 8806 %x".to_string(),
)?;
let operating_status = result[1].to_vec().unwrap();
let status_mode = result[0].to_i64().unwrap() as u8;
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
.await?;
if operating_status.len() < 2 {
return Err(InterfaceError::InvalidStatus(StatusError));
}
let operating_status_number =
(operating_status[0] as u16) << 8 | operating_status[1] as u16;
Ok((status_mode, operating_status_number))
}
pub async fn operating_status(&mut self) -> Result<u16, InterfaceError> {
let status = self.full_operating_status().await?.1;
Ok(status)
}
async fn playback_status_query(&mut self, p1: u32, p2: u32) -> Result<Vec<u8>, InterfaceError> {
self.change_descriptor_state(
&Descriptor::OperatingStatusBlock,
&DescriptorAction::OpenRead,
)
.await?;
let mut 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 res = scan_query(
reply,
"1809 8001 0330 %?%? %?%? %?%? %?%? %?%? %? 1000 00%?0000 %x %?".to_string(),
)?;
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
.await?;
Ok(res[0].to_vec().unwrap())
}
pub async fn playback_status1(&mut self) -> Result<Vec<u8>, InterfaceError> {
self.playback_status_query(0x8801, 0x8807).await
}
pub async fn playback_status2(&mut self) -> Result<Vec<u8>, InterfaceError> {
self.playback_status_query(0x8802, 0x8806).await
}
/// Get the current playback position
pub async fn position(&mut self) -> Result<[u16; 5], InterfaceError> {
self.change_descriptor_state(
&Descriptor::OperatingStatusBlock,
&DescriptorAction::OpenRead,
)
.await?;
let mut 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)
}
},
};
let result = scan_query(reply, "1809 8001 0430 %?%? %?%? %?%? %?%? %?%? %?%? %?%? %? %?00 00%?0000 000b 0002 0007 00 %w %B %B %B %B".to_string())?;
let final_result = [
result[0].to_i64().unwrap() as u16,
result[1].to_i64().unwrap() as u16,
result[2].to_i64().unwrap() as u16,
result[3].to_i64().unwrap() as u16,
result[4].to_i64().unwrap() as u16,
];
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 _reply = self.send_query(&mut 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();
match self.send_query(&mut query, true, false).await {
Ok(_) => Ok(true),
Err(error) => Err(error),
}
}
/// 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(
"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 res = scan_query(reply, "1850 00010000 0000 %w".to_string())?;
let value = res[0].to_i64().unwrap();
Ok(value as u16)
}
/// Move the playback to a specific time
pub async fn go_to_time(
&mut self,
track_number: u16,
hour: u8,
minute: u8,
second: u8,
frame: u8,
) -> Result<u16, InterfaceError> {
let mut query = format_query(
"1850 ff000000 0000 %w %B%B%B%B".to_string(),
vec![
QueryValue::Number(track_number as i64),
QueryValue::Number(hour as i64),
QueryValue::Number(minute as i64),
QueryValue::Number(second as i64),
QueryValue::Number(frame as i64),
],
)
.unwrap();
let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(reply, "1850 00000000 %?%? %w %B%B%B%B".to_string())?;
let value = res[0].to_i64().unwrap();
Ok(value as u16)
}
async fn track_change(&mut self, direction: Track) -> Result<(), InterfaceError> {
let mut 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?;
scan_query(reply, "1850 0010 00000000 %?%?".to_string())?;
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();
let reply = self.send_query(&mut 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
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 reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(reply, "1806 01101000 1000 0001000b %b".to_string()).unwrap();
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::Close)
.await?;
Ok(res[0].to_i64().unwrap() as u8)
}
/// 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 =
format_query("1806 02101001 3000 1000 ff00 00000000".to_string(), vec![]).unwrap();
let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(
reply,
"1806 02101001 %?%? %?%? 1000 00%?0000 0006 0010000200%b".to_string(),
)
.unwrap();
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close)
.await?;
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, InterfaceError> {
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
.await?;
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenRead)
.await?;
let mut done: i32 = 0;
let mut remaining: i32 = 0;
let mut total = 1;
let mut result: Vec<String> = Vec::new();
let mut chunksize;
let mut chunk;
while done < total {
let wchar_value = match wchar {
true => 1,
false => 0,
};
let mut query = format_query(
"1806 02201801 00%b 3000 0a00 ff00 %w%w".to_string(),
vec![
QueryValue::Number(wchar_value),
QueryValue::Number(remaining as i64),
QueryValue::Number(done as i64),
],
)
.unwrap();
let reply = self.send_query(&mut query, false, false).await?;
if remaining == 0 {
let res = scan_query(
reply,
"1806 02201801 00%? 3000 0a00 1000 %w0000 %?%?000a %w %*".to_string(),
)?;
chunksize = res[0].to_i64().unwrap() as i32;
total = res[1].to_i64().unwrap() as i32;
chunk = SHIFT_JIS.decode(&res[2].to_vec().unwrap()).0.into();
chunksize -= 6;
} else {
let res = scan_query(
reply,
"1806 02201801 00%? 3000 0a00 1000 %w%?%? %*".to_string(),
)?;
chunksize = res[0].to_i64().unwrap() as i32;
chunk = SHIFT_JIS.decode(&res[1].to_vec().unwrap()).0.into();
}
result.push(chunk);
done += chunksize;
remaining = total - done;
}
let res = result.join("");
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close)
.await?;
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close)
.await?;
Ok(res)
}
/// Gets the disc title
pub async fn disc_title(&mut self, wchar: bool) -> Result<String, InterfaceError> {
let mut title = self.raw_disc_title(wchar).await?;
let delim = match wchar {
true => "",
false => "//",
};
let title_marker = match wchar {
true => "",
false => "0;",
};
if title.ends_with(delim) {
let first_entry = title.split(delim).collect::<Vec<&str>>()[0];
if let Some(stripped_title) = first_entry.strip_prefix(title_marker) {
title = stripped_title.to_string();
} else {
title = String::new();
}
}
Ok(title)
}
/// Gets all groups on the disc
pub async fn track_group_list(
&mut self,
) -> Result<Vec<(Option<String>, Option<String>, Vec<u16>)>, InterfaceError> {
let raw_title = self.raw_disc_title(false).await?;
let group_list = raw_title.split("//");
let mut track_dict: HashMap<u16, (String, u16)> = HashMap::new();
let track_count = self.track_count().await?;
let mut result: Vec<(Option<String>, Option<String>, Vec<u16>)> = Vec::new();
let raw_full_title = self.raw_disc_title(true).await?;
let mut full_width_group_list = raw_full_title.split("");
for (i, group) in group_list.enumerate() {
if group.is_empty() {
continue;
}
if group.starts_with("0;") || group.find(';').is_none() || !raw_title.contains("//") {
continue;
}
let track_range: String = match group.split_once(';') {
Some(string) => string.0.to_string(),
None => return Err(InterfaceError::GroupError("no groups were found".into())),
};
if track_range.is_empty() {
continue;
}
let group_name = &group[track_range.len() + 1..];
let full_width_range = half_width_to_full_width_range(&track_range);
let full_width_group_name = full_width_group_list
.find(|n| n.starts_with(&full_width_range))
.unwrap()
.split_once('')
.unwrap()
.1;
let mut track_minmax: Vec<&str> = Vec::new();
if track_range.find('-').is_some() {
track_minmax = track_range.split('-').collect();
} else {
track_minmax.push(track_range.as_str());
}
let (track_min, mut track_max) = (
track_minmax[0].parse::<u16>().unwrap(),
track_minmax[1].parse::<u16>().unwrap(),
);
track_max = u16::min(track_max, track_count);
// TODO: Do some error handling here
assert!(track_min <= track_max);
let mut track_list: Vec<u16> = Vec::new();
for track in track_min - 1..track_max {
if track_dict.contains_key(&track) {
return Err(
InterfaceError::GroupError(format!("track {} is in 2 groups", track))
);
}
track_dict.insert(track, (String::from(group_name), i as u16));
track_list.push(track);
}
result.push((
Some(String::from(group_name)),
Some(String::from(full_width_group_name)),
track_list.clone(),
));
}
for i in 0..track_count {
if !track_dict.contains_key(&i) {
result.insert(0, (None, None, Vec::from([i])))
}
}
Ok(result)
}
/// Gets a list of track titles from a set
pub async fn track_titles(
&mut self,
tracks: Vec<u16>,
wchar: bool,
) -> Result<Vec<String>, InterfaceError> {
let wchar_value = match wchar {
true => 3,
false => 2,
};
let descriptor_type = match wchar {
true => Descriptor::AudioUTOC4TD,
false => Descriptor::AudioUTOC1TD,
};
self.change_descriptor_state(&descriptor_type, &DescriptorAction::OpenRead)
.await?;
let mut track_titles: Vec<String> = vec![];
for i in tracks {
let mut query = format_query(
"1806 022018%b %w 3000 0a00 ff00 00000000".to_string(),
vec![
QueryValue::Number(wchar_value),
QueryValue::Number(i as i64),
],
)
.unwrap();
let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(
reply,
"1806 022018%? %?%? %?%? %?%? 1000 00%?0000 00%?000a %x".to_string(),
)
.unwrap();
track_titles.push(
encoding_rs::SHIFT_JIS
.decode(&res[0].to_vec().unwrap())
.0
.into(),
)
}
self.change_descriptor_state(&descriptor_type, &DescriptorAction::Close)
.await?;
Ok(track_titles)
}
/// Gets the title of a single track at an index
pub async fn track_title(&mut self, track: u16, wchar: bool) -> Result<String, InterfaceError> {
let title = match self.track_titles([track].into(), wchar).await {
Ok(titles) => titles[0].clone(),
Err(error) if error.to_string() == "Rejected" => String::new(),
Err(error) => return Err(error),
};
Ok(title)
}
// Sets the title of the 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 {
return Err(InterfaceError::TitleError);
}
let new_title: Vec<u8>;
let old_len = length_after_encoding_to_jis(&current_title);
let wchar_value = match wchar {
true => {
new_title = sanitize_full_width_title(title, false);
1
}
false => {
new_title = sanitize_half_width_title(title);
0
}
};
let new_len = new_title.len();
if self.net_md_device.vendor_id() == 0x04dd {
self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::OpenWrite)
.await?
} else {
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close)
.await?;
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenWrite)
.await?
}
let mut query = format_query(
"1807 02201801 00%b 3000 0a00 5000 %w 0000 %w %*".to_string(),
vec![
QueryValue::Number(wchar_value),
QueryValue::Number(new_len as i64),
QueryValue::Number(old_len as i64),
QueryValue::Array(new_title),
],
)?;
let _ = self.send_query(&mut query, false, false).await;
if self.net_md_device.vendor_id() == 0x04dd {
self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::Close)
.await?
} else {
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close)
.await?;
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenRead)
.await?;
self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close)
.await?;
}
Ok(())
}
/// Sets the title of a track
pub async fn set_track_title(
&mut self,
track: u16,
title: &str,
wchar: bool,
) -> Result<(), InterfaceError> {
let new_title: Vec<u8>;
let (wchar_value, descriptor) = match wchar {
true => {
new_title = sanitize_full_width_title(title, false);
(3, Descriptor::AudioUTOC4TD)
}
false => {
new_title = sanitize_half_width_title(title);
(2, Descriptor::AudioUTOC1TD)
}
};
let new_len = new_title.len();
let old_len: u16 = match self.track_title(track, wchar).await {
Ok(current_title) => {
if title == current_title {
return Ok(());
}
length_after_encoding_to_jis(&current_title) as u16
}
Err(error) if error.to_string() == "Rejected" => 0,
Err(error) => return Err(error),
};
self.change_descriptor_state(&descriptor, &DescriptorAction::OpenWrite)
.await?;
let mut query = format_query(
"1807 022018%b %w 3000 0a00 5000 %w 0000 %w %*".to_string(),
vec![
QueryValue::Number(wchar_value),
QueryValue::Number(track as i64),
QueryValue::Number(new_len as i64),
QueryValue::Number(old_len as i64),
QueryValue::Array(new_title),
],
)?;
let reply = self.send_query(&mut query, false, false).await?;
let _ = scan_query(
reply,
"1807 022018%? %?%? 3000 0a00 5000 %?%? 0000 %?%?".to_string(),
);
self.change_descriptor_state(&descriptor, &DescriptorAction::Close)
.await?;
Ok(())
}
/// Erases a track from the disc's UTOC
pub async fn erase_track(&mut self, track: u16) -> Result<(), InterfaceError> {
let mut 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;
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(
"1843 ff00 00 201001 %w 201001 %w".to_string(),
vec![
QueryValue::Number(source as i64),
QueryValue::Number(dest as i64),
],
)?;
let _result = self.send_query(&mut query, false, false).await;
Ok(())
}
/// Raw information about a track
async fn raw_track_info(
&mut self,
track: u16,
p1: i32,
p2: i32,
) -> Result<Vec<u8>, InterfaceError> {
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
.await?;
let mut query = format_query(
"1806 02201001 %w %w %w ff00 00000000".to_string(),
vec![
QueryValue::Number(track as i64),
QueryValue::Number(p1 as i64),
QueryValue::Number(p2 as i64),
],
)?;
let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(
reply,
"1806 02201001 %?%? %?%? %?%? 1000 00%?0000 %x".to_string(),
)?;
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close)
.await?;
Ok(res[0].to_vec().unwrap())
}
/// Gets the length of tracks as a [std::time::Duration] from a set
pub async fn track_lengths(
&mut self,
tracks: Vec<u16>,
) -> Result<Vec<std::time::Duration>, InterfaceError> {
let mut times: Vec<std::time::Duration> = vec![];
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
.await?;
for track in tracks {
let mut query = format_query(
"1806 02201001 %w %w %w ff00 00000000".to_string(),
vec![
QueryValue::Number(track as i64),
QueryValue::Number(0x3000),
QueryValue::Number(0x0100),
],
)?;
let reply = self.send_query(&mut query, false, false).await?;
let res = scan_query(
reply,
"1806 02201001 %?%? %?%? %?%? 1000 00%?0000 %x".to_string(),
)?;
let result = scan_query(
res[0].to_vec().unwrap(),
"01 0006 0000 %B %B %B %B".to_string(),
)?;
let times_num: Vec<u64> = result
.into_iter()
.map(|v| v.to_i64().unwrap() as u64)
.collect();
let length = time_to_duration(&times_num);
times.push(length);
}
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close)
.await?;
Ok(times)
}
/// Gets the length of a track as a [std::time::Duration]
pub async fn track_length(
&mut self,
track: u16,
) -> Result<std::time::Duration, InterfaceError> {
Ok(self.track_lengths([track].into()).await?[0])
}
/// Gets the encoding of a track (SP, LP2, LP4)
pub async fn track_encoding(&mut self, track_number: u16) -> Result<Encoding, InterfaceError> {
let raw_value = self.raw_track_info(track_number, 0x3080, 0x0700).await?;
let result = scan_query(raw_value, "07 0004 0110 %b %b".to_string())?;
let final_encoding = match result[0].to_i64() {
Ok(0x90) => Encoding::SP,
Ok(0x92) => Encoding::LP2,
Ok(0x93) => Encoding::LP4,
Ok(e) => return Err(InterfaceError::InvalidEncoding(e as u8)),
Err(_) => unreachable!(),
};
Ok(final_encoding)
}
/// Gets a track's flags
pub async fn track_flags(&mut self, track: u16) -> Result<u8, InterfaceError> {
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead)
.await?;
let mut 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 res = scan_query(reply, "1806 01201001 %?%? 10 00 00010008 %b".to_string())?;
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close)
.await?;
Ok(res[0].to_i64().unwrap() as u8)
}
/// Gets the disc capacity as a [std::time::Duration]
pub async fn disc_capacity(&mut self) -> Result<[std::time::Duration; 3], InterfaceError> {
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 mut result: [std::time::Duration; 3] = [std::time::Duration::from_secs(0); 3];
// 8003 changed to %?03 - Panasonic returns 0803 instead. This byte's meaning is unknown
let res = scan_query(
reply,
"1806 02101000 3080 0300 1000 001d0000 001b %?03 0017 8000 0005 %W %B %B %B 0005 %W %B %B %B 0005 %W %B %B %B".to_string()
)?; //25^
let res_num: Vec<u64> = res
.into_iter()
.map(|v| v.to_i64().unwrap() as u64)
.collect();
// Create 3 values, `Frames Used`, `Frames Total`, and `Frames Left`
for i in 0..3 {
let tmp = &res_num[(4 * i)..=(4 * i) + 3];
let time_micros =
(tmp[0] * 3600000000) + (tmp[1] * 60000000) + (tmp[2] * 1000000) + (tmp[3] * 11600);
result[i] = std::time::Duration::from_micros(time_micros);
}
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::Close)
.await?;
Ok(result)
}
pub async fn recording_parameters(&mut self) -> Result<Vec<u8>, InterfaceError> {
self.change_descriptor_state(
&Descriptor::OperatingStatusBlock,
&DescriptorAction::OpenRead,
)
.await?;
let mut 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 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())?;
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close)
.await?;
Ok(res.into_iter().map(|x| x.to_i64().unwrap() as u8).collect())
}
/// Gets the bytes of a track
///
/// This can only be executed on an MZ-RH1 / M200
pub async fn save_track_to_array(
&mut self,
track: u16,
) -> Result<(DiscFormat, u16, Vec<u8>), InterfaceError> {
let mut 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 res = scan_query(
reply,
"1800 080046 f0030103 300000 1001 %w %b %d".to_string(),
)?;
let frames = res[0].to_i64().unwrap() as u16;
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?;
scan_query(
self.read_reply(false).await?,
"1800 080046 f003010330 0000 1001 %?%? %?%?".to_string(),
)?;
std::thread::sleep(std::time::Duration::from_millis(500));
let format: DiscFormat = match codec & 0x06 {
0 => DiscFormat::LP4,
2 => DiscFormat::LP2,
4 => DiscFormat::SPMono,
6 => DiscFormat::SPStereo,
e => return Err(InterfaceError::InvalidDiscFormat(e)),
};
Ok((format, frames, result))
}
pub async fn disable_new_track_protection(&mut self, val: u16) -> Result<(), InterfaceError> {
let mut 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?;
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 reply = self.send_query(&mut 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 reply = self.send_query(&mut query, false, false).await?;
scan_query(reply, "1800 080046 f0030103 81 00".to_string())?;
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
/// 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], InterfaceError> {
let mut query = format_query("1800 080046 f0030103 11 ff".to_string(), vec![])?;
let reply = self.send_query(&mut 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())
}
pub async fn send_key_data(
&mut self,
ekbid: i32,
keychain: [[u8; 16]; 2],
depth: i32,
ekbsignature: [u8; 24],
) -> Result<(), InterfaceError> {
let chainlen = keychain.len();
let databytes = 16 + 16 * chainlen + 24;
if !(1..=63).contains(&depth) {
return Err(EncryptionError::InvalidDepth(depth))?;
}
let keychains = keychain.concat();
let mut query = format_query(
"1800 080046 f0030103 12 ff %w 0000 %w %d %d %d 00000000 %* %*".to_string(),
vec![
QueryValue::Number(databytes as i64),
QueryValue::Number(databytes as i64),
QueryValue::Number(chainlen as i64),
QueryValue::Number(depth as i64),
QueryValue::Number(ekbid as i64),
QueryValue::Array(keychains),
QueryValue::Array(ekbsignature.to_vec()),
],
)?;
let reply = self.send_query(&mut query, false, false).await?;
scan_query(
reply,
"1800 080046 f0030103 12 01 %?%? %?%?%?%?".to_string(),
)?;
Ok(())
}
pub async fn session_key_exchange(
&mut self,
hostnonce: Vec<u8>,
) -> Result<Vec<u8>, InterfaceError> {
if hostnonce.len() != 8 {
return Err(EncryptionError::InvalidLength("host nonce", hostnonce.len()))?;
}
let mut 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 res = scan_query(reply, "1800 080046 f0030103 20 %? 000000 %#".to_string())?;
Ok(res[0].to_vec().unwrap())
}
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 reply = self.send_query(&mut query, false, false).await?;
let _ = scan_query(reply, "1800 080046 f0030103 21 00 000000".to_string())?;
Ok(())
}
pub async fn setup_download(
&mut self,
contentid: &[u8],
keyenckey: &[u8],
hex_session_key: &[u8],
) -> Result<(), InterfaceError> {
if contentid.len() != 20 {
return Err(EncryptionError::InvalidLength("content ID", contentid.len()))?;
}
if keyenckey.len() != 8 {
return Err(EncryptionError::InvalidLength("key encryption", keyenckey.len()))?;
}
if hex_session_key.len() != 8 {
return Err(EncryptionError::InvalidLength("session key", hex_session_key.len()))?;
}
let mut message = [vec![1, 1, 1, 1], contentid.to_vec(), keyenckey.to_vec()].concat();
DesCbcEnc::new(hex_session_key.into(), &[0u8; 8].into())
.encrypt_padded_mut::<NoPadding>(message.as_mut_slice(), 32)
.unwrap();
let mut 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?;
scan_query(reply, "1800 080046 f0030103 22 00 0000".to_string())?;
Ok(())
}
pub async fn commit_track(
&mut self,
track_number: u16,
hex_session_key: &[u8],
) -> Result<(), InterfaceError> {
if hex_session_key.len() != 8 {
return Err(EncryptionError::InvalidLength("hex session key", hex_session_key.len()))?;
}
let mut message = [0u8; 8];
DesEcbEnc::new(hex_session_key.into())
.encrypt_padded_mut::<NoPadding>(&mut message, 8)
.unwrap();
let mut query = format_query(
"1800 080046 f0030103 48 ff 00 1001 %w %*".to_string(),
vec![
QueryValue::Number(track_number as i64),
QueryValue::Array(Vec::from(message)),
],
)?;
let reply = self.send_query(&mut query, false, false).await?;
scan_query(reply, "1800 080046 f0030103 48 00 00 1001 %?%?".to_string())?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub async fn send_track<F>(
&mut self,
wireformat: u8,
discformat: u8,
frames: u32,
pkt_size: u32,
// key // iv // data
mut packets: UnboundedReceiver<(Vec<u8>, Vec<u8>, Vec<u8>)>,
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("hex session key", hex_session_key.len()))?;
}
// Sharps are slow
cross_sleep(Duration::from_millis(200)).await;
let total_bytes: usize = (pkt_size + 24) as usize; //framesizedict[wireformat] * frames + pktcount * 24;
let mut query = format_query(
"1800 080046 f0030103 28 ff 000100 1001 ffff 00 %b %b %d %d".to_string(),
vec![
QueryValue::Number(wireformat as i64),
QueryValue::Number(discformat as i64),
QueryValue::Number(frames as i64),
QueryValue::Number(total_bytes as i64),
],
)?;
let mut reply = self.send_query(&mut query, false, true).await?;
scan_query(
reply,
"1800 080046 f0030103 28 00 000100 1001 %?%? 00 %*".to_string(),
)?;
self.net_md_device.poll().await?;
// Sharps are slow
cross_sleep(Duration::from_millis(200)).await;
let mut _written_bytes = 0;
let mut packet_count = 0;
while let Some((key, iv, data)) = packets.recv().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()
} else {
data
};
self.net_md_device.write_bulk(&binpack).await?;
_written_bytes += binpack.len();
packet_count += 1;
(progress_callback)(total_bytes, _written_bytes);
if total_bytes == _written_bytes {
packets.close();
break;
}
}
reply = self.read_reply(false).await?;
self.net_md_device.poll().await?;
let res = scan_query(
reply,
"1800 080046 f0030103 28 00 000100 1001 %w 00 %?%? %?%?%?%? %?%?%?%? %*".to_string(),
)?;
let mut encrypted_data = res[1].to_vec().unwrap();
DesCbcDec::new(hex_session_key.into(), &[0u8; 8].into())
.decrypt_padded_mut::<NoPadding>(&mut encrypted_data)
.unwrap();
let part1 = encrypted_data[0..8].to_vec();
let part2 = encrypted_data[12..32].to_vec();
Ok((res[0].to_i64().unwrap() as u16, part1, part2))
}
pub async fn track_uuid(&mut self, track: u16) -> Result<String, InterfaceError> {
let mut 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 res = scan_query(reply, "1800 080046 f0030103 23 00 1001 %?%? %*".to_string())?;
Ok(String::from_utf8_lossy(&res[0].to_vec().unwrap()).to_string())
}
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?;
Ok(())
}
}
type DesEcbEnc = ecb::Encryptor<des::Des>;
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> {
let mut subkey_a = [0u8; 8];
subkey_a.clone_from_slice(&key[0..8]);
let mut beginning = [0u8; 8];
beginning.clone_from_slice(&value[0..8]);
let mut end = [0u8; 8];
end.clone_from_slice(&value[8..]);
DesCbcEnc::new(&subkey_a.into(), iv.into())
.encrypt_padded_mut::<NoPadding>(&mut beginning, 8)
.unwrap();
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]);
TDesCbcEnc::new(&wonky_key.into(), iv2.into())
.encrypt_padded_mut::<NoPadding>(&mut end, 8)
.unwrap();
end[..8].to_vec()
}
lazy_static! {
static ref DISC_FOR_WIRE: HashMap<WireFormat, DiscFormat> = HashMap::from([
(WireFormat::Pcm, DiscFormat::SPStereo),
(WireFormat::LP2, DiscFormat::LP2),
(WireFormat::L105kbps, DiscFormat::LP2),
(WireFormat::LP4, DiscFormat::LP4),
]);
}
pub struct EKBData {
chains: [[u8; 16]; 2],
depth: i32,
signature: [u8; 24],
}
pub struct EKBOpenSource {}
impl EKBOpenSource {
pub fn root_key(&self) -> [u8; 16] {
[
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65,
0x43, 0x21,
]
}
pub fn ekb_id(&self) -> i32 {
0x26422642
}
pub fn ekb_data_for_leaf_id(&self) -> EKBData {
EKBData {
chains: [
[
0x25, 0x45, 0x06, 0x4d, 0xea, 0xca, 0x14, 0xf9, 0x96, 0xbd, 0xc8, 0xa4, 0x06,
0xc2, 0x2b, 0x81,
],
[
0xfb, 0x60, 0xbd, 0xdd, 0x0d, 0xbc, 0xab, 0x84, 0x8a, 0x00, 0x5e, 0x03, 0x19,
0x4d, 0x3e, 0xda,
],
],
depth: 9,
signature: [
0x8f, 0x2b, 0xc3, 0x52, 0xe8, 0x6c, 0x5e, 0xd3, 0x06, 0xdc, 0xae, 0x18, 0xd2, 0xf3,
0x8c, 0x7f, 0x89, 0xb5, 0xe1, 0x85, 0x55, 0xa1, 0x05, 0xea,
],
}
}
}
pub struct MDTrack {
pub title: String,
pub format: WireFormat,
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 {
pub kek: [u8; 8],
pub frame_size: usize,
pub data: Vec<u8>,
pub chunk_size: usize,
}
impl MDTrack {
pub fn full_width_title(self) -> String {
self.full_width_title.unwrap_or("".to_string())
}
pub fn title(&self) -> String {
self.title.clone()
}
pub fn data_format(&self) -> WireFormat {
self.format.clone()
}
pub fn frame_count(&self) -> usize {
self.total_size() / self.frame_size()
}
pub fn frame_size(&self) -> usize {
*FRAME_SIZE.get(&self.format).unwrap()
}
pub fn chunk_size(&self) -> usize {
self.chunk_size
}
pub fn total_size(&self) -> usize {
let frame_size = self.frame_size();
let mut len = self.data.len();
if len % frame_size != 0 {
len = len + (frame_size - (len % frame_size));
}
len
}
pub fn content_id(&self) -> [u8; 20] {
[
0x01, 0x0f, 0x50, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xa2, 0x8d, 0x3e, 0x1a,
0x3b, 0x0c, 0x44, 0xaf, 0x2f, 0xa0,
]
}
pub fn get_kek(&self) -> [u8; 8] {
[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 {
kek: self.get_kek(),
frame_size: self.frame_size(),
chunk_size: self.chunk_size(),
data: std::mem::take(&mut self.data),
})
}
}
pub struct MDSession<'a> {
pub md: &'a mut NetMDInterface,
pub ekb_object: EKBOpenSource,
pub hex_session_key: Option<Vec<u8>>,
}
impl<'a> MDSession<'a> {
pub async fn init(&mut self) -> Result<(), Box<dyn Error>> {
self.md.enter_secure_session().await?;
self.md.leaf_id().await?;
let ekb = self.ekb_object.ekb_data_for_leaf_id();
self.md
.send_key_data(
self.ekb_object.ekb_id(),
ekb.chains,
ekb.depth,
ekb.signature,
)
.await?;
let mut nonce = vec![0u8; 8];
rand::thread_rng().fill_bytes(&mut nonce);
let mut devnonce = self.md.session_key_exchange(nonce.clone()).await?;
nonce.append(&mut devnonce);
self.hex_session_key = Some(retailmac(&self.ekb_object.root_key(), &nonce, &[0u8; 8]));
Ok(())
}
pub async fn close(&mut self) -> Result<(), Box<dyn Error>> {
if self.hex_session_key.is_none() {
self.md.session_key_forget().await?;
}
self.hex_session_key = None;
Ok(())
}
pub async fn download_track<F>(
&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());
}
self.md
.setup_download(
&track.content_id(),
&track.get_kek(),
self.hex_session_key.as_ref().unwrap(),
)
.await?;
let data_format = track.data_format();
let final_disc_format = disc_format.unwrap_or(*DISC_FOR_WIRE.get(&data_format).unwrap());
let (track_index, uuid, ccid) = self
.md
.send_track(
data_format as u8,
final_disc_format as u8,
track.frame_count() as u32,
track.total_size() as u32,
track.get_encrypting_iterator(),
self.hex_session_key.as_ref().unwrap().as_slice(),
progress_callback,
)
.await?;
self.md
.set_track_title(track_index, &track.title, false)
.await?;
if let Some(full_width) = track.full_width_title {
self.md
.set_track_title(track_index, &full_width, true)
.await?;
}
self.md
.commit_track(track_index, self.hex_session_key.as_ref().unwrap())
.await?;
Ok((track_index, uuid, ccid))
}
pub fn new(md: &'a mut NetMDInterface) -> Self {
MDSession {
md,
ekb_object: EKBOpenSource {},
hex_session_key: None,
}
}
}