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