diff --git a/himddump/himddump.c b/himddump/himddump.c index 48d5829..23f3baa 100644 --- a/himddump/himddump.c +++ b/himddump/himddump.c @@ -3,9 +3,13 @@ */ #include +#include #include #include #include +#include +#include +#include #include "himd.h" #include "sony_oma.h" @@ -21,9 +25,59 @@ void usage(char * cmdname) mp3key - show the MP3 encryption key for track \n\ dumptrack - dump track \n\ dumpmp3 - dump MP3 track \n\ - dumpnonmp3 - dump non-MP3 tracl \n", cmdname); + dumpnonmp3 - dump non-MP3 track \n\ + writemp3 - write mp3 to disc\n", cmdname); } +/* +static void print_hex(unsigned char* buf, size_t size) +{ + int i = 0; + int j = 0; + int breakpoint = 0; + + for(;i < (int)size; i++) + { + printf("%02x ", buf[i]); + breakpoint++; + if(!((i + 1)%16) && i) + { + printf("\t\t"); + for(j = ((i+1) - 16); j < ((i+1)/16) * 16; j++) + { + if(buf[j] < 30) + printf("."); + else + printf("%c", buf[j]); + } + printf("\n"); + breakpoint = 0; + } + } + + if(breakpoint == 16) + { + printf("\n"); + return; + } + + for(; breakpoint < 16; breakpoint++) + { + printf(" "); + } + printf("\t\t"); + + for(j = size - (size%16); j < (int)size; j++) + { + if(buf[j] < 30) + printf("."); + else + printf("%c", buf[j]); + } + printf("\n"); +} +*/ + static const char * hexdump(unsigned char * input, int len) { static char dumpspace[5][41]; @@ -326,6 +380,434 @@ void himd_dumpholes(struct himd * h) printf("%d: %05u-%05u\n", i, holes.holes[i].firstblock, holes.holes[i].lastblock); } + +void get_songinfo(const char *filepath, gchar ** artist, gchar ** title, gchar **album) +{ + // printf("DBG: get_songinfo()\n"); + struct id3_file * file; + struct id3_frame const *frame; + struct id3_tag *tag; + union id3_field const *field; + + file = id3_file_open(filepath , ID3_FILE_MODE_READONLY); + + tag = id3_file_tag(file); + if(!tag) + { + printf("no tags\n"); + id3_file_close(file); + return; + } + + // Artist + frame = id3_tag_findframe (tag, ID3_FRAME_ARTIST, 0); + if(frame && (field = &frame->fields[1])) + { + if(id3_field_getnstrings(field) > 0) + { + //printf("DBG: found artist\n"); + gchar *utf8 = NULL; + + utf8 = (gchar*) id3_ucs4_utf8duplicate( id3_field_getstrings(field,0)); + *artist = utf8; + // fix: utf8 buffer + + } + } + + // Title + frame = id3_tag_findframe (tag, ID3_FRAME_TITLE, 0); + if(frame && (field = &frame->fields[1])) + { + if(id3_field_getnstrings(field) > 0) + { + // printf("DBG: found title\n"); + gchar *utf8 = NULL; + + utf8 = (gchar*) id3_ucs4_utf8duplicate( id3_field_getstrings(field,0)); + *title = utf8; + // fix: utf8 buffer + } + } + + // Album + frame = id3_tag_findframe (tag, ID3_FRAME_ALBUM, 0); + if(frame && (field = &frame->fields[1])) + { + if(id3_field_getnstrings(field) > 0) + { + //printf("DBG: found album\n"); + gchar *utf8 = NULL; + + utf8 = (gchar*) id3_ucs4_utf8duplicate( id3_field_getstrings(field,0)); + *album = utf8; + // fix: utf8 buffer + } + } + + id3_file_close(file); +} + +void block_init(struct blockinfo * b, short int nframes, short int lendata, unsigned int serial_number, unsigned char * cid) +{ + b->nframes = nframes; + b->mcode = 0; + b->lendata = lendata; + b->reserved1 = 0; + b->serial_number = serial_number; + memset(&b->key, 0, 8); /* use zero key on mp3 files */ + // print_hex((unsigned char*)&b->key, 8); + memset(&b->iv, 0, 8); + memset(&b->backup_key, 0, 8); + strncpy((char*)&b->backup_type,"SPMA", 4); + memset(&b->reserved2, 0, 8); + b->backup_reserved = 0; + b->backup_mcode = 0; + b->lo32_contentid = cid[16]*16777216+cid[17]*65536+cid[18]*256+cid[19]; + b->backup_serial_number = serial_number; +} + +void block_printinfo(struct blockinfo * b) +{ + printf("block %d, nframes: %d, lendata: %d\n", + b->serial_number, b->nframes, b->lendata); +} + +struct abucket +{ + gint totsize; + gint nframes; + unsigned char *pbuf_current, *pbuf_end; + struct blockinfo block; +}; + +void bucket_init(struct abucket * pbucket) +{ + g_assert(pbucket != NULL); + memset(&pbucket->block, 0, sizeof(struct blockinfo)); + + pbucket->totsize = 0; + pbucket->nframes = 0; + pbucket->pbuf_current = &pbucket->block.audio_data[0]; + pbucket->pbuf_end = &pbucket->block.audio_data[HIMD_AUDIO_SIZE]; +} + +int bucket_append(struct abucket * pbucket, gchar * pframe, guint framelen) +{ + g_assert(pbucket != NULL); + g_assert(pframe != NULL); + + gint nbytes_to_add = framelen; + + // printf("totsize: %d, framelen: %d\n", pbucket->totsize, nbytes_to_add); + + // Buffer full? or too big frame for buffer? + if( (pbucket->totsize + nbytes_to_add) >= HIMD_AUDIO_SIZE) + { + if(pbucket->totsize == 0) + { + // printf("frame %d is too big for the block\n", pbucket->nframes); + return 0; + } + // printf("bucket_append: block is full, totsize: %d\n", pbucket->totsize); + // print_hex( &pbucket->block.audio_data[0], 100); + return -1; + } + + g_assert(pbucket->pbuf_current <= pbucket->pbuf_end); + + memcpy(pbucket->pbuf_current, pframe, nbytes_to_add); + + pbucket->pbuf_current += nbytes_to_add; + pbucket->totsize += nbytes_to_add; + pbucket->nframes += 1; + + return nbytes_to_add; +} + +// +// Input parameters: +// +// A opened mp3-stream, himd-write-stream, duration structure, (TODO) block-obfuscation-key +// +// Return values: +// +// Return the number of written blocks and frames +// +// Side-effects: +// +// Writes audio blocks at the end of the ATDATA container file. Audio blocks contains all frames (TODO: ID3 frames) +// in a obfuscated form using a 4 byte key. +// +gint write_blocks(struct mad_stream *stream, struct himd_writestream *write_stream, mp3key key, + mad_timer_t *duration, gint *nblocks, gint *nframes, unsigned char * cid, struct himderrinfo * status) +{ + struct abucket bucket; + struct mad_header header; + mad_timer_t mad_timer; + + gint iblock=0, iframe=0; + gint lenblocks=0; + + mad_timer_reset(&mad_timer); + bucket_init(&bucket); + + while(1) { + + if(mad_header_decode(&header, stream) == -1) { + // printf("## mad_frame_decode() error: %d, %s\n", stream->error, mad_stream_errorstr(stream)); + if(MAD_RECOVERABLE(stream->error)) + { + // printf("MAD_RECOVERABLE\n"); + continue; + } + else { + //printf("Unrecoverable error\n"); + break; + } + } + gchar * pframe = (gpointer) stream->this_frame; + gint framelen = (guint) (stream->next_frame - stream->this_frame); + + mad_timer_add(&mad_timer, header.duration); + + // + // DBG: frame read. + // + // printf("DBG(frame: %d): pframe: %p, framelen: %d\n", iframe, pframe, framelen); + // print_hex((void*) pframe, 10); + + // Append frames to block + gint nbytes_added = bucket_append(&bucket, pframe, framelen); + if(nbytes_added < 0) { + //printf("DBG: Bucket full!\n"); + block_init(&bucket.block, bucket.nframes, bucket.totsize, iblock, cid); + + // block_printinfo(&bucket.block); + + // obfuscate audio-data using key derived from track-number + // printf("\n Decrypted,first 30 bytes:\n"); + // print_hex(&bucket.block.audio_data[0], 30); + + // Encrypt block + int i=0; + for(i=0;i < bucket.totsize; i++) + bucket.block.audio_data[i] ^= key[i & 3]; + + // Append block to ATDATA file + if(himd_writestream_write(write_stream, &bucket.block, status) < 0) + { + fprintf(stderr, "Failed to write block: %d", iblock); + perror("write block"); + } + + lenblocks += bucket.totsize; + //printf("\n Encrypted, first 30 bytes: \n"); + // print_hex(&bucket.block.audio_data[0], 30); + + bucket_init(&bucket); + + // Append the frame to a new block, that not would fit in the previous full block + nbytes_added = bucket_append(&bucket, pframe, framelen); + if(nbytes_added < 0) { + //printf("ERROR: Unexpected full block\n"); + exit(1); + } + else if(nbytes_added == 0) { + //printf("DBG: Frame is too big for block\n"); + iframe++; // Skip frame + } + else + iframe++; + + iblock += 1; + continue; + } + else if(nbytes_added == 0) { + //printf("DBG: Frame is too big for block\n"); + bucket_init(&bucket); + iframe++; + continue; + } + else { + //printf("DBG: Added %i bytes\n", nbytes_added); + iframe++; + + } + } + + if( (nblocks != NULL) && (nframes != NULL) && (duration != NULL)) + { + *nblocks = iblock; + *nframes = iframe; + duration->seconds = mad_timer.seconds; + } + + // close write-stream to atdata file + return iblock; +} + +void himd_writemp3(struct himd *h, const char *filepath) +{ + struct himderrinfo status; + gint nblocks=0, nframes=0; + struct mad_stream stream; + mad_timer_t duration; + GMappedFile * mp3file; + unsigned long mp3size; + gchar * mp3buffer; + gchar * artist=NULL, * title=NULL, * album=NULL; + int i; + unsigned char cid[20] = {0x02, 0x03, 0x00, 0x00}; + + // Generate random content ID + for(i = 4; i <=19; i++) + cid[i] = g_random_int_range(0,0xFF); + + // Get track ID3 information + get_songinfo(filepath, &artist, &title, &album); + + // Load mp3 stream + mp3file = g_mapped_file_new(filepath, FALSE, NULL); + mp3size = g_mapped_file_get_length(mp3file); + mp3buffer = g_mapped_file_get_contents(mp3file); + + mad_stream_init(&stream); + mad_stream_buffer(&stream, (unsigned char*)mp3buffer, mp3size); + + // + // Get track-key using track-index + // + gint idx_track; + mp3key key; + idx_track = himd_get_free_trackindex(h); + + if(himd_obtain_mp3key(h, idx_track, &key, &status) < 0) + { + printf("Cannot obtain mp3key\n"); + exit(1); + } + // END: Get track-key + + // + // Write blocks to ATDATA + // + struct himd_writestream write_stream; + unsigned int first_blockno=0; + unsigned int last_blockno=0; + + if(himd_writestream_open(h, &write_stream, &first_blockno, &last_blockno, &status) < 0) + { + fprintf(stderr, "Error opening writestream\n"); + exit(1); + } + + write_blocks(&stream, &write_stream, key, &duration, &nblocks, &nframes, cid, &status); + + himd_writestream_close(&write_stream); + // END: Write blocks to ATDATA + + // + // Calculate blocknumber of the last written block + // + last_blockno = first_blockno + nblocks-1; + + // + // Add fragment descriptor, get back fragment number + // + struct fraginfo fragment; + gint idx_frag; + + fragment.firstblock = first_blockno; + fragment.lastblock = last_blockno; + memset(&fragment.key[0], 0, 8); + fragment.firstframe = 0; + fragment.lastframe = nframes; + fragment.fragtype = 1; + fragment.nextfrag = 0; + + idx_frag = himd_add_fragment_info(h, &fragment, &status); + // END: Add fragment + + // Add strings for title, album and artist strings. Retrieve string index numbers. + gint idx_title=0, idx_album=0, idx_artist=0; + + if(title != NULL) { + idx_title = himd_add_string(h, title, STRING_TYPE_TITLE, strlen(title)+1, &status); + if(idx_title < 0) + { + printf("Failed to add title string\n"); + exit(1); + } + } + + if(album != NULL) { + printf("hello\n"); + idx_album = himd_add_string(h, album, STRING_TYPE_ALBUM, strlen(album)+1, &status); + if(idx_album < 0) + { + printf("Failed to add album string\n"); + exit(1); + } + } + + if(artist != NULL) { + idx_artist = himd_add_string(h, artist, STRING_TYPE_ARTIST, strlen(artist)+1, &status); + if(idx_artist < 0) + { + printf("Failed to add artist string\n"); + exit(1); + } + } + // printf("DBG: idx_title: %d, idx_album: %d, idx_artist: %d\n", idx_title, idx_album, idx_artist); + // END: Add strings + + // + // Add track descriptor, get trackno back. + // + struct trackinfo track; + + memset(&track.key, 0, 8); + track.title = idx_title; + track.artist = idx_artist; + track.album = idx_album; + track.firstfrag = idx_frag; + track.tracknum = 1; + track.ekbnum = 0; + track.trackinalbum = 1; + track.codec_id = CODEC_ATRAC3PLUS_OR_MPEG; + track.seconds = duration.seconds; + memset(&track.codecinfo, 0, 5); + track.codecinfo[0] = 3; + + /* file dependent codec information, these values are for my test mp3 file only, */ + /* values fetched from trkidx file by downloading the same mp3 file with SonicStage */ + track.codecinfo[2] = 0xB0; + track.codecinfo[3] = 0xD9; + track.codecinfo[4] = 0x10; + + memset(&track.mac, 0, 8); + memcpy(&track.contentid, cid, 20); + memset(&track.recordingtime, 0, sizeof(struct tm)); + memset(&track.starttime, 0, sizeof(struct tm)); + memset(&track.endtime, 0, sizeof(struct tm)); + + /* set DRM stuff correctly for kompatibility reasons */ + track.Lt = 0x10; + track.Dest = 1; + track.Xcc = 1; + track.Cc = 0x40; + + idx_track = himd_add_track_info(h, &track, &status); + // END: Add track descriptor + + // + // Update TRACK-INDEX file with track strings, fragment descriptor and track-descriptor. + // + himd_write_tifdata(h, &status); + // free(artist); free(album); free(title); +} + int main(int argc, char ** argv) { int idx; @@ -382,6 +864,10 @@ int main(int argc, char ** argv) sscanf(argv[3], "%d", &idx); himd_dumpnonmp3(&h, idx); } + else if(strcmp(argv[2],"writemp3") == 0 && argc > 3) + { + himd_writemp3(&h, argv[3]); + } himd_close(&h); return 0; diff --git a/himddump/himddump.pro b/himddump/himddump.pro index e51463f..0736413 100644 --- a/himddump/himddump.pro +++ b/himddump/himddump.pro @@ -1,7 +1,8 @@ TEMPLATE=app CONFIG -= qt CONFIG += console link_pkgconfig link_prl -PKGCONFIG += glib-2.0 +PKGCONFIG += glib-2.0 id3tag +win32:LIBS += -lid3tag -lz INCLUDEPATH += ../libhimd SOURCES += himddump.c diff --git a/libhimd/himd.c b/libhimd/himd.c index 3e9c72c..7dff835 100644 --- a/libhimd/himd.c +++ b/libhimd/himd.c @@ -6,8 +6,9 @@ #define G_LOG_DOMAIN "HiMD" #include +#include #include - +#include #include "himd.h" #define _(x) (x) @@ -57,6 +58,46 @@ static int scanforatdata(GDir * dir) return maxdatanum; } + +// scan for TRKIDX files +static int scanfortif(GDir * dir, int* oldnum, int *newnum) +{ + const char * hmafile; + int found_blank=FALSE, found_unused=FALSE, found_used=FALSE; + int old_datanum, new_datanum; + + while((hmafile = g_dir_read_name(dir)) != NULL) + { + // Look for old version + if(!found_unused) + { + if(g_ascii_strncasecmp(hmafile,"_rkidx0",7) == 0 && + strlen(hmafile) == 12 && + isxdigit(hmafile[7]) && + g_ascii_strncasecmp(hmafile+8,".hma",4) == 0) + { + sscanf(hmafile+7,"%x",&old_datanum); + *oldnum = old_datanum; + found_unused = TRUE; + } + } + // Look for current version + if(!found_used) + { + if(g_ascii_strncasecmp(hmafile,"trkidx0",7) == 0 && + strlen(hmafile) == 12 && + isxdigit(hmafile[7]) && + g_ascii_strncasecmp(hmafile+8,".hma",4) == 0) + { + sscanf(hmafile+7,"%x",&new_datanum); + *newnum = new_datanum; + found_used = TRUE; + } + } + } + return (FALSE || found_unused || found_used); +} + static void nong_inplace_ascii_down(gchar * string) { while(*string) @@ -87,11 +128,79 @@ FILE * himd_open_file(struct himd * himd, const char * fileid) else nong_inplace_ascii_up(filename); filepath = g_build_filename(himd->rootpath,himd->need_lowercase ? "hmdhifi" : "HMDHIFI",filename,NULL); - file = fopen(filepath,"rb"); + file = fopen(filepath,"rb+"); g_free(filepath); return file; } + +int himd_write_tifdata(struct himd * himd, struct himderrinfo * status) +{ + char indexfilename[13], atdatafilename[13]; + gchar *unusedfile,*usedfile,*tempfile,*atdata; + gchar *filepath; + GDir * dir; + GError * error = NULL; + status = status; + + filepath = g_build_filename(himd->rootpath,himd->need_lowercase ? "hmdhifi" : "HMDHIFI", NULL); + dir = g_dir_open(filepath,0,&error); + int oldnum=0, newnum=0; + + if(scanfortif(dir, &oldnum, &newnum)) + { + sprintf(indexfilename, himd->need_lowercase ? "_rkidx%02x.hma" : "_RKIDX%02X.HMA", oldnum); + unusedfile = g_build_filename(himd->rootpath,himd->need_lowercase ? "hmdhifi" : "HMDHIFI", + indexfilename,NULL); + sprintf(indexfilename, himd->need_lowercase ? "trkidx%02x.hma" : "TRKIDX%02X.HMA", newnum); + usedfile = g_build_filename(himd->rootpath,himd->need_lowercase ? "hmdhifi" : "HMDHIFI", + indexfilename,NULL); + } + else + { + printf("didnt found any .TIF files\n"); + exit(1); + } + + // Setup filepaths to TRKIDX.TMP, TRKIDX01.HMA + tempfile = g_build_filename(himd->rootpath,himd->need_lowercase ? "hmdhifi" : "HMDHIFI", + "TRKIDX.TMP",NULL); + + // atdataXX.hma + sprintf(atdatafilename, himd->need_lowercase ? "atdata%02x.hma" : "ATDATA%02X.HMA", himd->datanum); + atdata = g_build_filename(himd->rootpath,himd->need_lowercase ? "hmdhifi" : "HMDHIFI", + atdatafilename,NULL); + if(!g_file_set_contents(unusedfile, (const char*)himd->tifdata, HIMD_TIFFILE_SIZE, &error)) + { + printf("Could not update unused TIFDATA file %s.\n", unusedfile); + exit(1); + } + + // unused -> tmp + // used -> unused + // tempfile -> used + + if(g_rename(unusedfile, tempfile) < 0) + { + printf("Could not rename blank unused %s to %s\n", unusedfile, tempfile); + exit(1); + } + if(g_rename(usedfile, unusedfile) < 0) + { + printf("Could not rename %s to %s\n", usedfile, tempfile); + exit(1); + } + if(g_rename(tempfile, usedfile) < 0) + { + printf("Could not rename %s to %s\n", tempfile, usedfile); + } + + g_free(filepath); + g_dir_close(dir); + + return 0; +} + static int himd_read_discid(struct himd * himd, struct himderrinfo * status) { FILE * mclistfile = himd_open_file(himd, "MCLIST"); diff --git a/libhimd/himd.h b/libhimd/himd.h index ae3f6ff..887f4a9 100644 --- a/libhimd/himd.h +++ b/libhimd/himd.h @@ -35,6 +35,10 @@ extern "C" { #define HIMD_ATRAC3_SAMPLES_PER_FRAME 1024 #define HIMD_ATRAC3P_SAMPLES_PER_FRAME 2048 +#define HIMD_TIFFILE_SIZE 327680 +#define HIMD_AUDIO_SIZE 0x3FC0 +#define HIMD_BLOCKINFO_SIZE 0x4000 + enum himdstatus { HIMD_OK, HIMD_STATUS_AUDIO_EOF, HIMD_ERROR_DISABLED_FEATURE, @@ -76,6 +80,12 @@ struct trackinfo { unsigned char contentid[20]; int ekbnum; struct tm recordingtime, starttime, endtime; + char Lt; + char Dest; + char Xcc; + char Ct; + char Cc; + char Cn; }; /* a fragment in the audio file */ @@ -89,6 +99,27 @@ struct fraginfo { unsigned int nextfrag; }; + +/* a block in the audio file */ +struct blockinfo { + unsigned int type; // "LPCM" or "A3D " or "ATX" or "SPMA" + short int nframes; + short int mcode; + short int lendata; + short int reserved1; + unsigned int serial_number; + unsigned char key[8]; + unsigned char iv[8]; + unsigned char audio_data[0x3FC0]; // obfuscated audio data + unsigned char backup_key[8]; + unsigned char reserved2[8]; + unsigned int backup_type; + short int backup_reserved; + short int backup_mcode; + int lo32_contentid; + int backup_serial_number; +}; + struct himdstring { char data[14]; unsigned int stringtype : 4; @@ -114,9 +145,11 @@ int himd_open(struct himd * himd, const char * himdroot, struct himderrinfo * st void himd_close(struct himd * himd); char* himd_get_string_raw(struct himd * himd, unsigned int idx, int*type, int* length, struct himderrinfo * status); char* himd_get_string_utf8(struct himd * himd, unsigned int idx, int*type, struct himderrinfo * status); +int himd_add_string(struct himd * himd, char *string, int type, int length, struct himderrinfo * status); void himd_free(void * p); const unsigned char * himd_get_discid(struct himd * himd, struct himderrinfo * status); FILE * himd_open_file(struct himd * himd, const char * fileid); +int himd_write_tifdata(struct himd * himd, struct himderrinfo * status); unsigned int himd_track_count(struct himd * himd); unsigned int himd_get_trackslot(struct himd * himd, int unsigned idx, struct himderrinfo * status); @@ -125,6 +158,10 @@ int himd_get_fragment_info(struct himd * himd, unsigned int idx, struct fraginfo int himd_track_uploadable(struct himd * himd, const struct trackinfo * track); int himd_track_blocks(struct himd * himd, const struct trackinfo * track, struct himderrinfo * status); +int himd_get_free_trackindex(struct himd * himd); +int himd_add_track_info(struct himd * himd, struct trackinfo * track, struct himderrinfo * status); +int himd_add_fragment_info(struct himd * himd, struct fraginfo * f, struct himderrinfo * status); + const char * himd_get_codec_name(const struct trackinfo * t); unsigned int himd_trackinfo_framesize(const struct trackinfo * track); unsigned int himd_trackinfo_framesperblock(const struct trackinfo * track); @@ -153,6 +190,19 @@ int himd_blockstream_read(struct himd_blockstream * stream, unsigned char * bloc unsigned int * firstframe, unsigned int * lastframe, unsigned char * fragkey, struct himderrinfo * status); + +struct himd_writestream { + struct himd * himd; + FILE * atdata; + unsigned int curblockno; +}; + +int himd_writestream_open(struct himd * himd, struct himd_writestream * stream, unsigned int * out_first_blockno, unsigned int * out_last_blockno, struct himderrinfo * status); + +int himd_writestream_write(struct himd_writestream * stream, struct blockinfo *block, struct himderrinfo * status); +void himd_writestream_close(struct himd_writestream * stream); + + struct himd_mp3stream { struct himd_blockstream stream; unsigned char blockbuf[16384]; diff --git a/libhimd/himd_private.h b/libhimd/himd_private.h index 9897aec..6f38059 100644 --- a/libhimd/himd_private.h +++ b/libhimd/himd_private.h @@ -8,6 +8,21 @@ static inline unsigned int beword32(const unsigned char * c) return c[0]*16777216+c[1]*65536+c[2]*256+c[3]; } + +static inline void setbeword16(unsigned char * c, unsigned int val) +{ + c[0] = val >> 8; + c[1] = val & 0xFF; +} + +static inline void setbeword32(unsigned char * c, unsigned int val) +{ + c[0] = (val >> 24) & 0xFF; + c[1] = (val >> 16) & 0xFF; + c[2] = (val >> 8) & 0xFF; + c[3] = val & 0xFF; +} + void set_status_const(struct himderrinfo * status, enum himdstatus code, const char * msg); void set_status_printf(struct himderrinfo * status, enum himdstatus code, const char * format, ...); diff --git a/libhimd/mdstream.c b/libhimd/mdstream.c index cfa7a95..86112de 100644 --- a/libhimd/mdstream.c +++ b/libhimd/mdstream.c @@ -151,6 +151,97 @@ int himd_blockstream_read(struct himd_blockstream * stream, unsigned char * bloc return 0; } +int himd_writestream_open(struct himd * himd, struct himd_writestream * stream, + unsigned int * out_first_blockno, unsigned int * out_last_blockno, struct himderrinfo * status) +{ + struct himd_holelist hole_list; + int firstblock, lastblock=0; + int block_offset=0; + + g_return_val_if_fail(himd != NULL, -1); + g_return_val_if_fail(stream != NULL, -1); + g_return_val_if_fail(status != NULL, -1); + + stream->himd = himd; + stream->atdata = himd_open_file(himd, "ATDATA"); + if(!stream->atdata) + { + fprintf(stderr, "Invalid filehandle %d", stream->atdata); + perror("DBG: cannot open ATDATA file for writing\n"); + return -1; + } + + // himd_find_holes + if( himd_find_holes(stream->himd, &hole_list, status) < 0) + { + puts(status->statusmsg); + exit(1); + } + + // get pointer to freespace + firstblock = hole_list.holes[0].firstblock; + lastblock = hole_list.holes[0].lastblock; + block_offset = firstblock * HIMD_BLOCKINFO_SIZE; + + // set position where to start writing blocks + if(fseek(stream->atdata, block_offset, SEEK_SET) != 0) + { + fprintf(stderr, "Error fseeking atdata\n"); + } + stream->curblockno = firstblock; + + if( (out_first_blockno != NULL) && (out_last_blockno != NULL) ) + { + *out_first_blockno = firstblock; + *out_last_blockno = lastblock; + } + + return 0; +} + +void himd_writestream_close(struct himd_writestream * stream) +{ + fclose(stream->atdata); +} + +static void setblock(struct blockinfo * b, unsigned char * blockbuffer) +{ + memset(blockbuffer, 0, HIMD_BLOCKINFO_SIZE); + strncpy((char*)blockbuffer, "SPMA", sizeof(unsigned int)); + setbeword16(blockbuffer+4, b->nframes); + setbeword16(blockbuffer+6, b->mcode); + setbeword16(blockbuffer+8, b->lendata); + setbeword32(blockbuffer+12, b->serial_number); + memcpy(blockbuffer+16, &b->key, 8); + memcpy(blockbuffer+24, &b->iv, 8); + memcpy(blockbuffer+32, &b->audio_data, HIMD_AUDIO_SIZE); + strncpy((char*)blockbuffer+16368, "SPMA", sizeof(unsigned int)); + setbeword16(blockbuffer+16374, b->mcode); + setbeword32(blockbuffer+16376, b->lo32_contentid); + setbeword32(blockbuffer+16380, b->serial_number); +} + +int himd_writestream_write(struct himd_writestream * stream, struct blockinfo * audioblock, struct himderrinfo *status) +{ + unsigned char data[HIMD_BLOCKINFO_SIZE]; + g_return_val_if_fail(stream != NULL, -1); + g_return_val_if_fail(audioblock != NULL, -1); + status = status; + stream = stream; + + // serialize the block descriptor + setblock(audioblock, data); + + // write the block descriptor to the current position in the stream at 'stream->curblockno' + if(fwrite(data, 16384, 1, stream->atdata) != 1) + { + perror("fwrite block\n"); + fprintf(stderr, "Error writing block to position %d\n", stream->curblockno); + return -1; + } + return 0; +} + int himd_mp3stream_open(struct himd * himd, unsigned int trackno, struct himd_mp3stream * stream, struct himderrinfo * status) { struct trackinfo trkinfo; diff --git a/libhimd/trackindex.c b/libhimd/trackindex.c index b3e051e..59f4d9d 100644 --- a/libhimd/trackindex.c +++ b/libhimd/trackindex.c @@ -58,6 +58,69 @@ static void get_dostime(struct tm * tm, unsigned const char * bytes) tm->tm_year = ((thedate & 0xFE00) >> 9) + 80; } + +static void dos_settime(unsigned char * buffer, const struct tm * tm) +{ + setbeword16(buffer, (tm->tm_mday) | + ((tm->tm_mon + 1) << 5) | + ((tm->tm_year - 80) << 9)); + setbeword16(buffer+2, (tm->tm_sec/2) | + (tm->tm_min << 5) | + (tm->tm_hour << 1)); +} + +static void settrack(struct trackinfo *t, unsigned char * trackbuffer) +{ + dos_settime(trackbuffer+0, &t->recordingtime); + setbeword32(trackbuffer+4, t->ekbnum); + setbeword16(trackbuffer+8, t->title); + setbeword16(trackbuffer+10, t->artist); + setbeword16(trackbuffer+12, t->album); + trackbuffer[14] = t->trackinalbum; + + memcpy(trackbuffer+16, t->key, 8); + memcpy(trackbuffer+24, t->mac, 8); + trackbuffer[32] = t->codec_id; + + memcpy(trackbuffer+33, t->codecinfo, 3); + memcpy(trackbuffer+44, t->codecinfo+3, 2); + + setbeword16(trackbuffer+36, t->firstfrag); + setbeword16(trackbuffer+38, t->tracknum); + setbeword16(trackbuffer+40, t->seconds); + + memcpy(trackbuffer+48, t->contentid, 20); + dos_settime(trackbuffer+68, &t->starttime); + dos_settime(trackbuffer+72, &t->endtime); + + /* DRM stuff */ + trackbuffer[42] = t->Lt; + trackbuffer[43] = t->Dest; + trackbuffer[76] = t->Xcc; + trackbuffer[78] = t->Cc; +} + +static void setfrag(struct fraginfo *f, unsigned char * fragbuffer) +{ + memcpy(fragbuffer, &f->key, 8); + setbeword16(fragbuffer+8, f->firstblock); + setbeword16(fragbuffer+10, f->lastblock); + fragbuffer[12] = f->firstframe; + fragbuffer[13] = f->lastframe; + setbeword16(fragbuffer+14,f->nextfrag); +} + +int himd_get_free_trackindex(struct himd * himd) +{ + int idx_freeslot; + unsigned char * linkbuffer; + + linkbuffer = get_track(himd, 0); + idx_freeslot = beword16(&linkbuffer[38]); + + return idx_freeslot; +} + int himd_get_track_info(struct himd * himd, unsigned int idx, struct trackinfo * t, struct himderrinfo * status) { unsigned char * trackbuffer; @@ -97,6 +160,43 @@ int himd_get_track_info(struct himd * himd, unsigned int idx, struct trackinfo * return 0; } + +int himd_add_track_info(struct himd * himd, struct trackinfo * t, struct himderrinfo * status) +{ + int idx_freeslot; + unsigned char * linkbuffer; + unsigned char * trackbuffer; + unsigned char * play_order_table = himd->tifdata+0x100; + + status = status; + + g_return_val_if_fail(himd != NULL, -1); + g_return_val_if_fail(t != NULL, -1); + + /* get track[0] - the free-chain index */ + linkbuffer = get_track(himd, 0); + idx_freeslot = beword16(&linkbuffer[38]); + + /* allocate slot idx_freeslot for the new track*/ + trackbuffer = get_track(himd, idx_freeslot); + t->tracknum = idx_freeslot; + + /* update track[] - free-chain index */ + setbeword16(&linkbuffer[38], beword16(&trackbuffer[38])); + + /* copy trackinfo to slot */ + settrack(t, trackbuffer); + + /* increase track count */ + setbeword16(play_order_table, himd_track_count(himd)+1); + + /* add entry for new track in play order table */ + setbeword16(play_order_table+2*idx_freeslot, t->tracknum); + + return idx_freeslot; +} + + const char * himd_get_codec_name(const struct trackinfo * track) { static char buffer[5]; @@ -206,6 +306,32 @@ int himd_get_fragment_info(struct himd * himd, unsigned int idx, struct fraginfo return 0; } + +int himd_add_fragment_info(struct himd * himd, struct fraginfo * f, struct himderrinfo * status) +{ + int idx_freefrag; + unsigned char * linkbuffer; + unsigned char * fragbuffer; + status = status; + + g_return_val_if_fail(himd != NULL, -1); + g_return_val_if_fail(f != NULL, -1); + + linkbuffer = get_frag(himd, 0); + + idx_freefrag = beword16(linkbuffer+14) & 0xFFF; + fragbuffer = get_frag(himd, idx_freefrag); + + setbeword16(linkbuffer+14, beword16(fragbuffer+14) & 0xFFF); + f->nextfrag = 0; + + /* copy fragment struct to slot buffer */ + setfrag(f, fragbuffer); + + return idx_freefrag; +} + + char* himd_get_string_raw(struct himd * himd, unsigned int idx, int*type, int* length, struct himderrinfo * status) { int curidx; @@ -316,3 +442,119 @@ char* himd_get_string_utf8(struct himd * himd, unsigned int idx, int*type, struc } return out; } + + +int himd_add_string(struct himd * himd, char *string, int type, int length, struct himderrinfo * status) +{ + int curidx, lastidx; + int stridx, end_stridx; + int nslots=0, rest=0; + int idx_freeslot; + unsigned char * linkchunk; + unsigned char * freeslot; + unsigned char * curchunk; + + const char * current_charset; + int strencoding=0; + + g_return_val_if_fail(himd != NULL, -1); + g_return_val_if_fail(string != NULL, -1); + + + if(g_get_charset(¤t_charset)) + strencoding = HIMD_ENCODING_LATIN1; + else if(g_ascii_strncasecmp(current_charset, "UTF16BE", 7) == 0) + strencoding = HIMD_ENCODING_UTF16BE; + else if(g_ascii_strncasecmp(current_charset, "SHIFT_JIS", 9) == 0) + strencoding = HIMD_ENCODING_SHIFT_JIS; + else + set_status_printf(status, HIMD_ERROR_UNKNOWN_ENCODING, + "unknown encoding"); + + /* how many number of slots to store string in? */ + + if(length <= 13) + { + nslots = 1; + } + else if(length > 13) + { + nslots = 1; + nslots += (length-13) / 14; + rest = (length-13) % 14; + if(rest) + nslots+=1; + } + else + { + } + + /* get index to first free slot in array */ + linkchunk = get_strchunk(himd, 0); + idx_freeslot = strlink(linkchunk); + + /* update index link to next free chunk */ + setbeword16(&linkchunk[14], idx_freeslot+nslots); + + /* head slot of where string will be stored */ + freeslot = get_strchunk(himd, idx_freeslot); + + /* indexes to keep track of copying string into slots + (slots[1],...,slot[N]; (N-1) nr of slots) */ + + curidx = idx_freeslot; + lastidx = (idx_freeslot + nslots) - 1; + stridx = 0; + end_stridx = nslots; + + g_return_val_if_fail(curidx > 0, -1); + g_return_val_if_fail(curidx < 4096, -1); + g_return_val_if_fail(lastidx < 4096, -1); + + /* need the string any continuation slots ?*/ + if(length <= 13) + { + curchunk = get_strchunk(himd, curidx); + curchunk[0] = strencoding; + + setbeword16(&curchunk[14], (type << 12) + 0x00); // type | 00 + memcpy(curchunk+1, &string[0], length); + + return idx_freeslot; + } + /* nslots-1 continuation slots is needed */ + else + { + /* + slot-head + nslots > 1 && (rest == 0 || rest > 0) + */ + curchunk = get_strchunk(himd, curidx); + curchunk[0] = strencoding; + + setbeword16(&curchunk[14], (type << 12) + (curidx+1)); // type | lnk + memcpy(curchunk+1, &string[0], 13); + + curidx += 1; + stridx += 1; + } + + for (; curidx < (lastidx+1) && (stridx < end_stridx); + curidx += 1, stridx += 1) + { + curchunk = get_strchunk(himd, curidx); + + /* reached the last continuation slot ? */ + if(curidx == lastidx) + { + setbeword16(&curchunk[14], (STRING_TYPE_CONTINUATION << 12) + 0); // type | lnk + memcpy(curchunk, &string[stridx*14], (rest == 0 ? 14 : rest) ); + break; + } + + setbeword16(&curchunk[14], (STRING_TYPE_CONTINUATION << 12)+(curidx+1)); // type | lnk + memcpy(curchunk, &string[stridx*14], 14); + } + + return idx_freeslot; +}