[linux-minidisc] netmd/netmdcli: download support for any .wav file containing pcm audio
Hi,
i made some changes to libnetmd and netmdcli to support download of
audio files in pcm format (filename: *.wav, codec: pcm, 16 bit, 4100 Hz,
stereo).
Codec used on the minidisc is Atrac SP stereo, but this can be changed
anytime if wanted.
Thomas
>From 7c9b7bc6f6dc87a0d7fb421a271cbde63026a6df Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Wed, 1 Jan 2014 20:30:24 +0100
Subject: [PATCH 1/1] support netmd downloads for any .wav file containing
pcm(16 bit, 4100 Hz, stereo) audio data
---
libnetmd/secure.c | 17 ++--
libnetmd/secure.h | 4 +-
netmdcli/netmdcli.c | 222 ++++++++++++++++++++++++++++++++-------------------
3 files changed, 151 insertions(+), 92 deletions(-)
diff --git a/libnetmd/secure.c b/libnetmd/secure.c
index a3dbb6e..e770571 100644
--- a/libnetmd/secure.c
+++ b/libnetmd/secure.c
@@ -370,8 +370,8 @@ void netmd_transfer_song_packets(netmd_dev_handle *dev,
memcpy(buf + 8, p->iv, 8);
memcpy(buf + 16, p->data, p->length);
- /* ... send it */
- error = libusb_bulk_transfer((libusb_device_handle*)dev, 2, packet, (int)packet_size, &transferred, 10000);
+ /* ... send it, increased timeout, 10000 is too low for larger files */
+ error = libusb_bulk_transfer((libusb_device_handle*)dev, 2, packet, (int)packet_size, &transferred, 80000);
netmd_log(NETMD_LOG_DEBUG, "%d %d\n", packet_size, error);
/* cleanup */
@@ -387,11 +387,12 @@ void netmd_transfer_song_packets(netmd_dev_handle *dev,
netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
netmd_track_packets **packets,
- size_t *packet_count,
- unsigned char *key_encryption_key)
+ size_t *packet_count, size_t *frames,
+ unsigned char *key_encryption_key, netmd_wireformat format)
{
size_t position = 0;
size_t chunksize = 0xffffffffU;
+ size_t frame_size = netmd_get_frame_size(format);
netmd_track_packets *last = NULL;
netmd_track_packets *next = NULL;
@@ -417,9 +418,9 @@ netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
/* limit chunksize for last packet */
chunksize = data_lenght - position;
}
-
- if ((chunksize % 8) != 0) {
- chunksize = chunksize + 8 - (chunksize % 8);
+ /* do not truncate frames */
+ if ((chunksize % frame_size) != 0) {
+ chunksize = chunksize + frame_size - (chunksize % frame_size);
}
/* alloc memory */
@@ -459,6 +460,8 @@ netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
gcry_cipher_close(key_handle);
gcry_cipher_close(data_handle);
+ *frames = position/frame_size;
+
return error;
}
diff --git a/libnetmd/secure.h b/libnetmd/secure.h
index 41e41ce..50a7060 100644
--- a/libnetmd/secure.h
+++ b/libnetmd/secure.h
@@ -191,8 +191,8 @@ netmd_error netmd_secure_delete_track(netmd_dev_handle *dev, uint16_t track,
netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
netmd_track_packets **packets,
- size_t *packet_count,
- unsigned char *key_encryption_key);
+ size_t *packet_count, size_t *frames,
+ unsigned char *key_encryption_key, netmd_wireformat format);
void netmd_cleanup_packets(netmd_track_packets **packets);
diff --git a/netmdcli/netmdcli.c b/netmdcli/netmdcli.c
index 354d03c..41559e2 100644
--- a/netmdcli/netmdcli.c
+++ b/netmdcli/netmdcli.c
@@ -168,6 +168,26 @@ void retailmac(unsigned char *rootkey, unsigned char *hostnonce,
gcry_cipher_close(handle2);
}
+static inline unsigned int leword32(const unsigned char * c)
+{
+ return c[3]*16777216+c[2]*65536+c[1]*256+c[0];
+}
+
+static int wav_data_position(const char * data, size_t len)
+{
+ int pos = -1, i = 0;
+ while(pos < 0)
+ {
+ if(i >= len-4) // break at end of data
+ break;
+
+ if(strcmp("data", data+i) == 0)
+ pos = i;
+ i+=2;
+ }
+ return pos;
+}
+
int main(int argc, char* argv[])
{
netmd_dev_handle* devh;
@@ -444,104 +464,140 @@ int main(int argc, char* argv[])
uint16_t track;
unsigned char uuid[8] = { 0 };
unsigned char new_contentid[20] = { 0 };
+ char title[256] = {0};
- error = netmd_secure_leave_session(devh);
- puts(netmd_strerror(error));
+ size_t frames;
+ int data_position, audio_data_position, audio_data_size, i, file_valid = 0;
+ unsigned char * audio_data;
+ netmd_wireformat wireformat = NETMD_WIREFORMAT_PCM;
+ unsigned char discformat = NETMD_DISKFORMAT_SP_STEREO;
- error = netmd_secure_set_track_protection(devh, 0x01);
- puts(netmd_strerror(error));
-
- error = netmd_secure_enter_session(devh);
- puts(netmd_strerror(error));
+ /* read source */
+ stat(argv[2], &stat_buf);
+ data_size = (size_t)stat_buf.st_size;
+ data = malloc(data_size);
+ f = fopen(argv[2], "rb");
+ fread(data, data_size, 1, f);
+ fclose(f);
- /* build ekb */
- ekb.id = 0x26422642;
- ekb.depth = 9;
- ekb.signature = malloc(sizeof(signature));
- memcpy(ekb.signature, signature, sizeof(signature));
+ /* TODO: check file for codec, PCM (16 bit, 44100 Hz, stereo) should work correctly*/
+ if((data_position = wav_data_position((char *)data, data_size)) <= 0)
+ {
+ puts("Error: invalid audio file, cannot find data header position");
+ free(data);
+ }
+ else
+ {
+ audio_data_position = data_position+8;
+ audio_data = data+audio_data_position;
+ audio_data_size = leword32(data+(data_position+4));
+ file_valid = 1;
+ }
- /* build ekb key chain */
- ekb.chain = NULL;
- for (done = 0; done < sizeof(chain); done+=16U)
+ if(file_valid)
{
- next = malloc(sizeof(netmd_keychain));
- if (ekb.chain == NULL) {
- ekb.chain = next;
- }
- else {
- keychain->next = next;
- }
- next->next = NULL;
+ error = netmd_secure_leave_session(devh);
+ puts(netmd_strerror(error));
- next->key = malloc(16);
- memcpy(next->key, chain + done, 16);
+ error = netmd_secure_set_track_protection(devh, 0x01);
+ puts(netmd_strerror(error));
- keychain = next;
- }
+ error = netmd_secure_enter_session(devh);
+ puts(netmd_strerror(error));
- error = netmd_secure_send_key_data(devh, &ekb);
- puts(netmd_strerror(error));
-
- /* cleanup */
- free(ekb.signature);
- keychain = ekb.chain;
- while (keychain != NULL) {
- next = keychain->next;
- free(keychain->key);
- free(keychain);
- keychain = next;
- }
+ /* build ekb */
+ ekb.id = 0x26422642;
+ ekb.depth = 9;
+ ekb.signature = malloc(sizeof(signature));
+ memcpy(ekb.signature, signature, sizeof(signature));
- /* exchange nonces */
- gcry_create_nonce(hostnonce, sizeof(hostnonce));
- error = netmd_secure_session_key_exchange(devh, hostnonce, devnonce);
- puts(netmd_strerror(error));
+ /* build ekb key chain */
+ ekb.chain = NULL;
+ for (done = 0; done < sizeof(chain); done+=16U)
+ {
+ next = malloc(sizeof(netmd_keychain));
+ if (ekb.chain == NULL) {
+ ekb.chain = next;
+ }
+ else {
+ keychain->next = next;
+ }
+ next->next = NULL;
+
+ next->key = malloc(16);
+ memcpy(next->key, chain + done, 16);
+
+ keychain = next;
+ }
- /* calculate session key */
- retailmac(rootkey, hostnonce, devnonce, sessionkey);
+ error = netmd_secure_send_key_data(devh, &ekb);
+ puts(netmd_strerror(error));
+
+ /* cleanup */
+ free(ekb.signature);
+ keychain = ekb.chain;
+ while (keychain != NULL) {
+ next = keychain->next;
+ free(keychain->key);
+ free(keychain);
+ keychain = next;
+ }
- error = netmd_secure_setup_download(devh, contentid, kek, sessionkey);
- puts(netmd_strerror(error));
+ /* exchange nonces */
+ gcry_create_nonce(hostnonce, sizeof(hostnonce));
+ error = netmd_secure_session_key_exchange(devh, hostnonce, devnonce);
+ puts(netmd_strerror(error));
- /* read source */
- stat(argv[2], &stat_buf);
- data_size = (size_t)stat_buf.st_size;
- data = malloc(data_size);
- f = fopen(argv[2], "rb");
- fseek(f, 60, SEEK_CUR);
- fread(data, data_size - 60, 1, f);
- fclose(f);
- error = netmd_prepare_packets(data, data_size-60, &packets, &packet_count, kek);
- puts(netmd_strerror(error));
-
- /* send to device */
- error = netmd_secure_send_track(devh, NETMD_WIREFORMAT_LP2,
- NETMD_DISKFORMAT_LP2,
- (data_size - 60) / 192, packets,
- packet_count, sessionkey,
- &track, uuid, new_contentid);
- puts(netmd_strerror(error));
-
- /* cleanup */
- netmd_cleanup_packets(&packets);
-
- /* set title */
- netmd_log(NETMD_LOG_DEBUG, "New Track: %d\n", track);
- netmd_cache_toc(devh);
- netmd_set_title(devh, track, "test");
- netmd_sync_toc(devh);
+ /* calculate session key */
+ retailmac(rootkey, hostnonce, devnonce, sessionkey);
- /* commit track */
- error = netmd_secure_commit_track(devh, track, sessionkey);
- puts(netmd_strerror(error));
+ error = netmd_secure_setup_download(devh, contentid, kek, sessionkey);
+ puts(netmd_strerror(error));
- /* forget key */
- error = netmd_secure_session_key_forget(devh);
- puts(netmd_strerror(error));
+ /* audio data byte order conversion, .wav files are little endian, need big endian for pcm raw data*/
+ for(i = 0; i < audio_data_size/2; i+=2)
+ {
+ unsigned char first = audio_data[i];
+ audio_data[i] = audio_data[i+1];
+ audio_data[i+1] = first;
+ }
- /* leave session */
- error = netmd_secure_leave_session(devh);
- puts(netmd_strerror(error));
+ /* netmd_prepare_packets() sets correct number of frames depending on the wire format */
+ error = netmd_prepare_packets(audio_data, audio_data_size, &packets, &packet_count, &frames, kek, wireformat);
+ puts(netmd_strerror(error));
+
+ /* send to device */
+ error = netmd_secure_send_track(devh, wireformat,
+ discformat,
+ frames, packets,
+ packet_count, sessionkey,
+ &track, uuid, new_contentid);
+ puts(netmd_strerror(error));
+
+ /* cleanup */
+ netmd_cleanup_packets(&packets);
+ free(data);
+ audio_data = NULL;
+
+ /* set title, use filename */
+ memcpy(title, argv[2], strlen(argv[2])-4);
+ netmd_log(NETMD_LOG_DEBUG, "New Track: %d\n", track);
+ netmd_cache_toc(devh);
+ netmd_set_title(devh, track, title);
+ netmd_sync_toc(devh);
+
+ /* commit track */
+ error = netmd_secure_commit_track(devh, track, sessionkey);
+ puts(netmd_strerror(error));
+
+ /* forget key */
+ error = netmd_secure_session_key_forget(devh);
+ puts(netmd_strerror(error));
+
+ /* leave session */
+ error = netmd_secure_leave_session(devh);
+ puts(netmd_strerror(error));
+ }
}
else if(strcmp("help", argv[1]) == 0)
{
--
1.7.10.4