void ASFByteIO::asf_byteio_getGUID(asf_guid_t *guid, uint8_t *data) { guid->v1 = asf_byteio_getDWLE(data); guid->v2 = asf_byteio_getWLE(data + 4); guid->v3 = asf_byteio_getWLE(data + 6); memcpy(guid->v4, data + 8, 8); }
void ASFByteIO::asf_byteio_get_string(uint16_t *string, uint16_t strlen, uint8_t *data) { int i; if (!data || !string) return; for (i=0; i<strlen; i++) { string[i] = asf_byteio_getWLE(data + i*2); } }
/** * Reads the stream properties object's data into the equivalent * data structure, and stores it in asf_stream_t structure * with the equivalent stream type. Needs the stream properties * object data as its input. */ static int asf_parse_header_stream_properties(asf_stream_t *stream, uint8_t *objdata, uint32_t objsize) { asf_guid_t guid; guid_type_t type; uint32_t datalen; uint8_t *data; if (objsize < 78) { return ASF_ERROR_INVALID_LENGTH; } asf_byteio_getGUID(&guid, objdata); type = asf_guid_get_stream_type(&guid); datalen = asf_byteio_getDWLE(objdata + 40); if (datalen > objsize - 78) { return ASF_ERROR_INVALID_LENGTH; } data = objdata + 54; if (type == GUID_STREAM_TYPE_EXTENDED) { /* FIXME: Need to find out what actually is here... but for now we can just skip the extended part */ if (datalen < 64) return ASF_ERROR_INVALID_LENGTH; data += 64; datalen -= 64; /* update the stream type with correct one */ asf_byteio_getGUID(&guid, objdata); type = asf_guid_get_stream_type(&guid); } switch (type) { case GUID_STREAM_TYPE_AUDIO: case GUID_STREAM_TYPE_EXTENDED_AUDIO: { asf_waveformatex_t *wfx; stream->type = ASF_STREAM_TYPE_AUDIO; if (datalen < 18) { return ASF_ERROR_INVALID_LENGTH; } if (asf_byteio_getWLE(data + 16) > datalen - 16) { return ASF_ERROR_INVALID_LENGTH; } /* this should be freed in asf_close function */ stream->properties = malloc(sizeof(asf_waveformatex_t)); if (!stream->properties) return ASF_ERROR_OUTOFMEM; stream->flags |= ASF_STREAM_FLAG_AVAILABLE; wfx = stream->properties; wfx->wFormatTag = asf_byteio_getWLE(data); wfx->nChannels = asf_byteio_getWLE(data + 2); wfx->nSamplesPerSec = asf_byteio_getDWLE(data + 4); wfx->nAvgBytesPerSec = asf_byteio_getDWLE(data + 8); wfx->nBlockAlign = asf_byteio_getWLE(data + 12); wfx->wBitsPerSample = asf_byteio_getWLE(data + 14); wfx->cbSize = asf_byteio_getWLE(data + 16); wfx->data = data + 18; if (wfx->cbSize > datalen - 18) { debug_printf("Invalid waveformatex data length, truncating!"); wfx->cbSize = datalen - 18; } break; } case GUID_STREAM_TYPE_VIDEO: { asf_bitmapinfoheader_t *bmih; uint32_t width, height, flags, data_size; stream->type = ASF_STREAM_TYPE_VIDEO; if (datalen < 51) { return ASF_ERROR_INVALID_LENGTH; } width = asf_byteio_getDWLE(data); height = asf_byteio_getDWLE(data + 4); flags = data[8]; data_size = asf_byteio_getWLE(data + 9); data += 11; datalen -= 11; if (asf_byteio_getDWLE(data) != datalen) { return ASF_ERROR_INVALID_LENGTH; } if (width != asf_byteio_getDWLE(data + 4) || height != asf_byteio_getDWLE(data + 8) || flags != 2) { return ASF_ERROR_INVALID_VALUE; } /* this should be freed in asf_close function */ stream->properties = malloc(sizeof(asf_bitmapinfoheader_t)); if (!stream->properties) return ASF_ERROR_OUTOFMEM; stream->flags |= ASF_STREAM_FLAG_AVAILABLE; bmih = stream->properties; bmih->biSize = asf_byteio_getDWLE(data); bmih->biWidth = asf_byteio_getDWLE(data + 4); bmih->biHeight = asf_byteio_getDWLE(data + 8); bmih->biPlanes = asf_byteio_getDWLE(data + 12); bmih->biBitCount = asf_byteio_getDWLE(data + 14); bmih->biCompression = asf_byteio_getDWLE(data + 16); bmih->biSizeImage = asf_byteio_getDWLE(data + 20); bmih->biXPelsPerMeter = asf_byteio_getDWLE(data + 24); bmih->biYPelsPerMeter = asf_byteio_getDWLE(data + 28); bmih->biClrUsed = asf_byteio_getDWLE(data + 32); bmih->biClrImportant = asf_byteio_getDWLE(data + 36); bmih->data = data + 40; if (bmih->biSize > datalen) { debug_printf("Invalid bitmapinfoheader data length, truncating!"); bmih->biSize = datalen; } break; } case GUID_STREAM_TYPE_COMMAND: stream->type = ASF_STREAM_TYPE_COMMAND; break; default: stream->type = ASF_STREAM_TYPE_UNKNOWN; break; } return 0; }
/** * Allocates a metadata struct and parses the contents from * the header object raw data. All strings are in UTF-8 encoded * format. The returned struct needs to be freed using the * asf_header_metadata_destroy function. Returns NULL on failure. */ asf_metadata_t * asf_header_metadata(asf_object_header_t *header) { asfint_object_t *current; asf_metadata_t *ret; /* allocate the metadata struct */ ret = calloc(1, sizeof(asf_metadata_t)); if (!ret) { return NULL; } current = asf_header_get_object(header, GUID_CONTENT_DESCRIPTION); if (current) { char *str; uint16_t strlen; int i, read = 0; /* The validity of the object is already checked so we can assume * there's always enough data to read and there are no overflows */ for (i=0; i<5; i++) { strlen = asf_byteio_getWLE(current->data + i*2); if (!strlen) continue; str = asf_utf8_from_utf16le(current->data + 10 + read, strlen); read += strlen; switch (i) { case 0: ret->title = str; break; case 1: ret->artist = str; break; case 2: ret->copyright = str; break; case 3: ret->description = str; break; case 4: ret->rating = str; break; default: free(str); break; } } } current = asf_header_get_object(header, GUID_EXTENDED_CONTENT_DESCRIPTION); if (current) { int i, j, position; ret->extended_count = asf_byteio_getWLE(current->data); ret->extended = calloc(ret->extended_count, sizeof(asf_metadata_entry_t)); if (!ret->extended) { /* Clean up the already allocated parts and return */ free(ret->title); free(ret->artist); free(ret->copyright); free(ret->description); free(ret->rating); free(ret); return NULL; } position = 2; for (i=0; i<ret->extended_count; i++) { uint16_t length, type; length = asf_byteio_getWLE(current->data + position); position += 2; ret->extended[i].key = asf_utf8_from_utf16le(current->data + position, length); position += length; type = asf_byteio_getWLE(current->data + position); position += 2; length = asf_byteio_getWLE(current->data + position); position += 2; switch (type) { case 0: /* type of the value is a string */ ret->extended[i].value = asf_utf8_from_utf16le(current->data + position, length); break; case 1: /* type of the value is a data block */ ret->extended[i].value = malloc((length*2 + 1) * sizeof(char)); for (j=0; j<length; j++) { static const char hex[16] = "0123456789ABCDEF"; ret->extended[i].value[j*2+0] = hex[current->data[position]>>4]; ret->extended[i].value[j*2+1] = hex[current->data[position]&0x0f]; } ret->extended[i].value[j*2] = '\0'; break; case 2: /* type of the value is a boolean */ ret->extended[i].value = malloc(6 * sizeof(char)); sprintf(ret->extended[i].value, "%s", *current->data ? "true" : "false"); break; case 3: /* type of the value is a signed 32-bit integer */ ret->extended[i].value = malloc(11 * sizeof(char)); sprintf(ret->extended[i].value, "%u", asf_byteio_getDWLE(current->data + position)); break; case 4: /* FIXME: This doesn't print whole 64-bit integer */ ret->extended[i].value = malloc(21 * sizeof(char)); sprintf(ret->extended[i].value, "%u", (uint32_t) asf_byteio_getQWLE(current->data + position)); break; case 5: /* type of the value is a signed 16-bit integer */ ret->extended[i].value = malloc(6 * sizeof(char)); sprintf(ret->extended[i].value, "%u", asf_byteio_getWLE(current->data + position)); break; default: /* Unknown value type... */ ret->extended[i].value = NULL; break; } position += length; } } return ret; }
/** * Reads the file properties object contents to the asf_file_t structure, * parses the useful values from stream properties object to the equivalent * stream properties info structure and validates that all known header * subobjects have only legal values. */ int asf_parse_header_validate(asf_file_t *file, asf_object_header_t *header) { /* some flags for mandatory subobjects */ int fileprop = 0, streamprop = 0; asfint_object_t *current; if (header->first) { current = header->first; while (current) { uint64_t size = current->size; switch (current->type) { case GUID_FILE_PROPERTIES: { uint32_t max_packet_size; if (size < 104) return ASF_ERROR_OBJECT_SIZE; if (fileprop) { /* multiple file properties objects not allowed */ return ASF_ERROR_INVALID_OBJECT; } fileprop = 1; asf_byteio_getGUID(&file->file_id, current->data); file->file_size = asf_byteio_getQWLE(current->data + 16); file->creation_date = asf_byteio_getQWLE(current->data + 24); file->data_packets_count = asf_byteio_getQWLE(current->data + 32); file->play_duration = asf_byteio_getQWLE(current->data + 40); file->send_duration = asf_byteio_getQWLE(current->data + 48); file->preroll = asf_byteio_getQWLE(current->data + 56); file->flags = asf_byteio_getDWLE(current->data + 64); file->packet_size = asf_byteio_getDWLE(current->data + 68); file->max_bitrate = asf_byteio_getQWLE(current->data + 76); max_packet_size = asf_byteio_getDWLE(current->data + 72); if (file->packet_size != max_packet_size) { /* in ASF file minimum packet size and maximum * packet size have to be same apparently... * stupid, eh? */ return ASF_ERROR_INVALID_VALUE; } break; } case GUID_STREAM_PROPERTIES: { uint16_t flags; asf_stream_t *stream; int ret; if (size < 78) return ASF_ERROR_OBJECT_SIZE; streamprop = 1; flags = asf_byteio_getWLE(current->data + 48); stream = &file->streams[flags & 0x7f]; if (stream->type) { /* only one stream object per stream allowed */ return ASF_ERROR_INVALID_OBJECT; } ret = asf_parse_header_stream_properties(stream, current->data, size); if (ret < 0) { return ret; } break; } case GUID_CONTENT_DESCRIPTION: { uint32_t stringlen = 0; if (size < 34) return ASF_ERROR_OBJECT_SIZE; stringlen += asf_byteio_getWLE(current->data); stringlen += asf_byteio_getWLE(current->data + 2); stringlen += asf_byteio_getWLE(current->data + 4); stringlen += asf_byteio_getWLE(current->data + 6); stringlen += asf_byteio_getWLE(current->data + 8); if (size < stringlen + 34) { /* invalid string length values */ return ASF_ERROR_INVALID_LENGTH; } break; } case GUID_MARKER: break; case GUID_CODEC_LIST: if (size < 44) return ASF_ERROR_OBJECT_SIZE; break; case GUID_STREAM_BITRATE_PROPERTIES: if (size < 26) return ASF_ERROR_OBJECT_SIZE; break; case GUID_PADDING: break; case GUID_EXTENDED_CONTENT_DESCRIPTION: if (size < 26) return ASF_ERROR_OBJECT_SIZE; break; case GUID_UNKNOWN: /* unknown guid type */ break; default: /* identified type in wrong place */ return ASF_ERROR_INVALID_OBJECT; } current = current->next; } } if (header->ext) { current = header->ext->first; while (current) { uint64_t size = current->size; switch (current->type) { case GUID_METADATA: if (size < 26) return ASF_ERROR_OBJECT_SIZE; break; case GUID_LANGUAGE_LIST: if (size < 26) return ASF_ERROR_OBJECT_SIZE; break; case GUID_EXTENDED_STREAM_PROPERTIES: { uint16_t stream_num; asf_stream_t *stream; int ret; if (size < 88) return ASF_ERROR_OBJECT_SIZE; stream_num = asf_byteio_getWLE(current->data + 48); stream = &file->streams[stream_num]; ret = asf_parse_header_extended_stream_properties(stream, current->data, size); if (ret < 0) { return ret; } break; } case GUID_ADVANCED_MUTUAL_EXCLUSION: if (size < 42) return ASF_ERROR_OBJECT_SIZE; break; case GUID_STREAM_PRIORITIZATION: if (size < 26) return ASF_ERROR_OBJECT_SIZE; break; case GUID_UNKNOWN: /* unknown guid type */ break; default: /* identified type in wrong place */ break; } current = current->next; } } if (!fileprop || !streamprop || !header->ext) { /* mandatory subobject missing */ return ASF_ERROR_INVALID_OBJECT; } return 1; }
static int asf_parse_header_extended_stream_properties(asf_stream_t *stream, uint8_t *objdata, uint32_t objsize) { asf_stream_extended_t ext; uint32_t datalen; uint8_t *data; uint16_t flags; int i; ext.start_time = asf_byteio_getQWLE(objdata); ext.end_time = asf_byteio_getQWLE(objdata + 8); ext.data_bitrate = asf_byteio_getDWLE(objdata + 16); ext.buffer_size = asf_byteio_getDWLE(objdata + 20); ext.initial_buf_fullness = asf_byteio_getDWLE(objdata + 24); ext.data_bitrate2 = asf_byteio_getDWLE(objdata + 28); ext.buffer_size2 = asf_byteio_getDWLE(objdata + 32); ext.initial_buf_fullness2 = asf_byteio_getDWLE(objdata + 36); ext.max_obj_size = asf_byteio_getDWLE(objdata + 40); ext.flags = asf_byteio_getDWLE(objdata + 44); ext.stream_num = asf_byteio_getWLE(objdata + 48); ext.lang_idx = asf_byteio_getWLE(objdata + 50); ext.avg_time_per_frame = asf_byteio_getQWLE(objdata + 52); ext.stream_name_count = asf_byteio_getWLE(objdata + 60); ext.num_payload_ext = asf_byteio_getWLE(objdata + 62); datalen = objsize - 88; data = objdata + 64; /* iterate through all name strings */ for (i=0; i<ext.stream_name_count; i++) { uint16_t strlen; if (datalen < 4) { return ASF_ERROR_INVALID_VALUE; } strlen = asf_byteio_getWLE(data + 2); if (strlen > datalen) { return ASF_ERROR_INVALID_LENGTH; } /* skip the current name string */ data += 4 + strlen; datalen -= 4 + strlen; } /* iterate through all extension systems */ for (i=0; i<ext.num_payload_ext; i++) { uint32_t extsyslen; if (datalen < 22) { return ASF_ERROR_INVALID_VALUE; } extsyslen = asf_byteio_getDWLE(data + 18); if (extsyslen > datalen) { return ASF_ERROR_INVALID_LENGTH; } /* skip the current extension system */ data += 22 + extsyslen; datalen -= 22 + extsyslen; } if (datalen > 0) { asf_guid_t guid; debug_printf("hidden stream properties object found!"); /* this is almost same as in stream properties handler */ if (datalen < 78) { return ASF_ERROR_OBJECT_SIZE; } /* check that we really have a stream properties object */ asf_byteio_getGUID(&guid, data); if (asf_guid_get_type(&guid) != GUID_STREAM_PROPERTIES) { return ASF_ERROR_INVALID_OBJECT; } if (asf_byteio_getQWLE(data + 16) != datalen) { return ASF_ERROR_OBJECT_SIZE; } flags = asf_byteio_getWLE(data + 72); if ((flags & 0x7f) != ext.stream_num || stream->type) { /* only one stream object per stream allowed and * stream ids have to match with both objects*/ return ASF_ERROR_INVALID_OBJECT; } else { int ret; stream->flags |= ASF_STREAM_FLAG_HIDDEN; ret = asf_parse_header_stream_properties(stream, data + 24, datalen); if (ret < 0) { return ret; } } } stream->extended = malloc(sizeof(asf_stream_extended_t)); if (!stream->extended) { return ASF_ERROR_OUTOFMEM; } stream->flags |= ASF_STREAM_FLAG_EXTENDED; memcpy(stream->extended, &ext, sizeof(ext)); return 0; }
static int asf_parse_header(int fd, struct mp3entry* id3, asf_waveformatex_t* wfx) { asf_object_t current; asf_object_t header; uint64_t datalen; int i; int fileprop = 0; uint64_t play_duration; uint16_t flags; uint32_t subobjects; uint8_t utf8buf[512]; int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); unsigned char* id3buf = (unsigned char*)id3->id3v2buf; asf_read_object_header((asf_object_t *) &header, fd); //DEBUGF("header.size=%d\n",(int)header.size); if (header.size < 30) { /* invalid size for header object */ return ASF_ERROR_OBJECT_SIZE; } read_uint32le(fd, &subobjects); /* Two reserved bytes - do we need to read them? */ lseek(fd, 2, SEEK_CUR); //DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects); if (subobjects > 0) { header.datalen = header.size - 30; /* TODO: Check that we have datalen bytes left in the file */ datalen = header.datalen; for (i=0; i<(int)subobjects; i++) { //DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen); if (datalen < 24) { //DEBUGF("not enough data for reading object\n"); break; } asf_read_object_header(¤t, fd); if (current.size > datalen || current.size < 24) { //DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen); break; } if (asf_guid_match(¤t.guid, &asf_guid_file_properties)) { if (current.size < 104) return ASF_ERROR_OBJECT_SIZE; if (fileprop) { /* multiple file properties objects not allowed */ return ASF_ERROR_INVALID_OBJECT; } fileprop = 1; /* Get the number of logical packets - uint64_t at offset 32 * (little endian byte order) */ lseek(fd, 32, SEEK_CUR); read_uint64le(fd, &wfx->numpackets); /*DEBUGF("read packets: %llx %lld\n", wfx->numpackets, wfx->numpackets);*/ /* Now get the play duration - uint64_t at offset 40 */ read_uint64le(fd, &play_duration); id3->length = play_duration / 10000; /*DEBUGF("****** length = %lums\n", id3->length);*/ /* Read the packet size - uint32_t at offset 68 */ lseek(fd, 20, SEEK_CUR); read_uint32le(fd, &wfx->packet_size); /* Skip bytes remaining in object */ lseek(fd, current.size - 24 - 72, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_stream_properties)) { guid_t guid; uint32_t propdatalen; if (current.size < 78) return ASF_ERROR_OBJECT_SIZE; #if 0 asf_byteio_getGUID(&guid, current->data); datalen = asf_byteio_getDWLE(current->data + 40); flags = asf_byteio_getWLE(current->data + 48); #endif asf_readGUID(fd, &guid); lseek(fd, 24, SEEK_CUR); read_uint32le(fd, &propdatalen); lseek(fd, 4, SEEK_CUR); read_uint16le(fd, &flags); if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) { //DEBUGF("Found stream properties for non audio stream, skipping\n"); lseek(fd,current.size - 24 - 50,SEEK_CUR); } else if (wfx->audiostream == -1) { lseek(fd, 4, SEEK_CUR); //DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f); if (propdatalen < 18) { return ASF_ERROR_INVALID_LENGTH; } #if 0 if (asf_byteio_getWLE(data + 16) > datalen - 16) { return ASF_ERROR_INVALID_LENGTH; } #endif read_uint16le(fd, &wfx->codec_id); read_uint16le(fd, &wfx->channels); read_uint32le(fd, &wfx->rate); read_uint32le(fd, &wfx->bitrate); wfx->bitrate *= 8; read_uint16le(fd, &wfx->blockalign); read_uint16le(fd, &wfx->bitspersample); read_uint16le(fd, &wfx->datalen); /*sanity check the included bitrate by comparing to file size and length*/ unsigned int estimated_bitrate = (wfx->packet_size*wfx->numpackets)/id3->length*8000; /*in theory we could just use the estimated bitrate always, but its safer to underestimate*/ if( wfx->bitrate > estimated_bitrate) { /* Round bitrate to the nearest kbit */ id3->bitrate = (estimated_bitrate + 500) / 1000; } else { /* Round bitrate to the nearest kbit */ id3->bitrate = (wfx->bitrate + 500) / 1000; } /*DEBUGF("bitrate: %d estimated: %d\n", wfx->bitrate, estimated_bitrate);*/ id3->frequency = wfx->rate; if (wfx->codec_id == ASF_CODEC_ID_WMAV1) { read(fd, wfx->data, 4); lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR); wfx->audiostream = flags&0x7f; } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) { read(fd, wfx->data, 6); lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR); wfx->audiostream = flags&0x7f; } else if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) { /* wma pro decoder needs the extra-data */ read(fd, wfx->data, wfx->datalen); lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); wfx->audiostream = flags&0x7f; /* Correct codectype to redirect playback to the proper .codec */ id3->codectype = AFMT_WMAPRO; } else if (wfx->codec_id == ASF_CODEC_ID_WMAVOICE) { read(fd, wfx->data, wfx->datalen); lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); wfx->audiostream = flags&0x7f; id3->codectype = AFMT_WMAVOICE; } else { DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); lseek(fd,current.size - 24 - 72,SEEK_CUR); } } } else if (asf_guid_match(¤t.guid, &asf_guid_content_description)) { /* Object contains five 16-bit string lengths, followed by the five strings: title, artist, copyright, description, rating */ uint16_t strlength[5]; int i; //DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24)); /* Read the 5 string lengths - number of bytes included trailing zero */ for (i=0; i<5; i++) { read_uint16le(fd, &strlength[i]); //DEBUGF("strlength = %u\n",strlength[i]); } if (strlength[0] > 0) { /* 0 - Title */ id3->title = id3buf; asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining); } if (strlength[1] > 0) { /* 1 - Artist */ id3->artist = id3buf; asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining); } lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */ if (strlength[3] > 0) { /* 3 - description */ id3->comment = id3buf; asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining); } lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */ } else if (asf_guid_match(¤t.guid, &asf_guid_extended_content_description)) { uint16_t count; int i; int bytesleft = current.size - 24; //DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n"); read_uint16le(fd, &count); bytesleft -= 2; //DEBUGF("extended metadata count = %u\n",count); for (i=0; i < count; i++) { uint16_t length, type; unsigned char* utf8 = utf8buf; int utf8length = 512; read_uint16le(fd, &length); asf_utf16LEdecode(fd, length, &utf8, &utf8length); bytesleft -= 2 + length; read_uint16le(fd, &type); read_uint16le(fd, &length); if (!strcmp("WM/TrackNumber",utf8buf)) { if (type == 0) { id3->track_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); id3->tracknum = atoi(id3->track_string); } else if ((type >=2) && (type <= 5)) { id3->tracknum = asf_intdecode(fd, type, length); } else { lseek(fd, length, SEEK_CUR); } } else if ((!strcmp("WM/Genre", utf8buf)) && (type == 0)) { id3->genre_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/AlbumTitle", utf8buf)) && (type == 0)) { id3->album = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/AlbumArtist", utf8buf)) && (type == 0)) { id3->albumartist = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/Composer", utf8buf)) && (type == 0)) { id3->composer = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if (!strcmp("WM/Year", utf8buf)) { if (type == 0) { id3->year_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); id3->year = atoi(id3->year_string); } else if ((type >=2) && (type <= 5)) { id3->year = asf_intdecode(fd, type, length); } else { lseek(fd, length, SEEK_CUR); } } else if (!strncmp("replaygain_", utf8buf, 11)) { char *value = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); parse_replaygain(utf8buf, value, id3); } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { id3->mb_track_id = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); #ifdef HAVE_ALBUMART } else if (!strcmp("WM/Picture", utf8buf)) { uint32_t datalength, strlength; /* Expected is either "01 00 xx xx 03 yy yy yy yy" or * "03 yy yy yy yy". xx is the size of the WM/Picture * container in bytes. yy equals the raw data length of * the embedded image. */ lseek(fd, -4, SEEK_CUR); read(fd, &type, 1); if (type == 1) { lseek(fd, 3, SEEK_CUR); read(fd, &type, 1); /* In case the parsing will fail in the next step we * might at least be able to skip the whole section. */ datalength = length - 1; } if (type == 3) { /* Read the raw data length of the embedded image. */ read_uint32le(fd, &datalength); /* Reset utf8 buffer */ utf8 = utf8buf; utf8length = 512; /* Gather the album art format, this string has a * double zero-termination. */ asf_utf16LEdecode(fd, 32, &utf8, &utf8length); strlength = (strlen(utf8buf) + 2) * 2; lseek(fd, strlength-32, SEEK_CUR); if (!strcmp("image/jpeg", utf8buf)) { id3->albumart.type = AA_TYPE_JPG; } else if (!strcmp("image/jpg", utf8buf)) { /* image/jpg is technically invalid, * but it does occur in the wild */ id3->albumart.type = AA_TYPE_JPG; } else if (!strcmp("image/png", utf8buf)) { id3->albumart.type = AA_TYPE_PNG; } else { id3->albumart.type = AA_TYPE_UNKNOWN; } /* Set the album art size and position. */ if (id3->albumart.type != AA_TYPE_UNKNOWN) { id3->albumart.pos = lseek(fd, 0, SEEK_CUR); id3->albumart.size = datalength; id3->has_embedded_albumart = true; } } lseek(fd, datalength, SEEK_CUR); #endif } else { lseek(fd, length, SEEK_CUR); } bytesleft -= 4 + length; } lseek(fd, bytesleft, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_content_encryption) || asf_guid_match(¤t.guid, &asf_guid_extended_content_encryption)) { //DEBUGF("File is encrypted\n"); return ASF_ERROR_ENCRYPTED; } else { //DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24)); lseek(fd,current.size - 24,SEEK_CUR); } //DEBUGF("Parsed object - size = %d\n",(int)current.size); datalen -= current.size; } if (i != (int)subobjects || datalen != 0) { //DEBUGF("header data doesn't match given subobject count\n"); return ASF_ERROR_INVALID_VALUE; } //DEBUGF("%d subobjects read successfully\n", i); } #if 0 tmp = asf_parse_header_validate(file, &header); if (tmp < 0) { /* header read ok but doesn't validate correctly */ return tmp; } #endif //DEBUGF("header validated correctly\n"); return 0; }
static int asf_parse_header(int fd, struct mp3entry* id3, asf_waveformatex_t* wfx) { asf_object_t current; asf_object_t header; uint64_t datalen; int i; int fileprop = 0; uint64_t play_duration; uint16_t flags; uint32_t subobjects; uint8_t utf8buf[512]; int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); unsigned char* id3buf = (unsigned char*)id3->id3v2buf; asf_read_object_header((asf_object_t *) &header, fd); //DEBUGF("header.size=%d\n",(int)header.size); if (header.size < 30) { /* invalid size for header object */ return ASF_ERROR_OBJECT_SIZE; } read_uint32le(fd, &subobjects); /* Two reserved bytes - do we need to read them? */ lseek(fd, 2, SEEK_CUR); //DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects); if (subobjects > 0) { header.datalen = header.size - 30; /* TODO: Check that we have datalen bytes left in the file */ datalen = header.datalen; for (i=0; i<(int)subobjects; i++) { //DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen); if (datalen < 24) { //DEBUGF("not enough data for reading object\n"); break; } asf_read_object_header(¤t, fd); if (current.size > datalen || current.size < 24) { //DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen); break; } if (asf_guid_match(¤t.guid, &asf_guid_file_properties)) { if (current.size < 104) return ASF_ERROR_OBJECT_SIZE; if (fileprop) { /* multiple file properties objects not allowed */ return ASF_ERROR_INVALID_OBJECT; } fileprop = 1; /* Get the number of logical packets - uint16_t at offset 31 * (Big endian byte order) */ lseek(fd, 31, SEEK_CUR); read_uint16be(fd, &wfx->numpackets); /* Now get the play duration - uint64_t at offset 40 */ lseek(fd, 7, SEEK_CUR); read_uint64le(fd, &play_duration); id3->length = play_duration / 10000; //DEBUGF("****** length = %lums\n", id3->length); /* Read the packet size - uint32_t at offset 68 */ lseek(fd, 20, SEEK_CUR); read_uint32le(fd, &wfx->packet_size); /* Skip bytes remaining in object */ lseek(fd, current.size - 24 - 72, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_stream_properties)) { guid_t guid; uint32_t propdatalen; if (current.size < 78) return ASF_ERROR_OBJECT_SIZE; #if 0 asf_byteio_getGUID(&guid, current->data); datalen = asf_byteio_getDWLE(current->data + 40); flags = asf_byteio_getWLE(current->data + 48); #endif asf_readGUID(fd, &guid); lseek(fd, 24, SEEK_CUR); read_uint32le(fd, &propdatalen); lseek(fd, 4, SEEK_CUR); read_uint16le(fd, &flags); if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) { //DEBUGF("Found stream properties for non audio stream, skipping\n"); lseek(fd,current.size - 24 - 50,SEEK_CUR); } else if (wfx->audiostream == -1) { lseek(fd, 4, SEEK_CUR); //DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f); if (propdatalen < 18) { return ASF_ERROR_INVALID_LENGTH; } #if 0 if (asf_byteio_getWLE(data + 16) > datalen - 16) { return ASF_ERROR_INVALID_LENGTH; } #endif read_uint16le(fd, &wfx->codec_id); read_uint16le(fd, &wfx->channels); read_uint32le(fd, &wfx->rate); read_uint32le(fd, &wfx->bitrate); wfx->bitrate *= 8; read_uint16le(fd, &wfx->blockalign); read_uint16le(fd, &wfx->bitspersample); read_uint16le(fd, &wfx->datalen); /* Round bitrate to the nearest kbit */ id3->bitrate = (wfx->bitrate + 500) / 1000; id3->frequency = wfx->rate; if (wfx->codec_id == ASF_CODEC_ID_WMAV1) { read(fd, wfx->data, 4); lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR); wfx->audiostream = flags&0x7f; } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) { read(fd, wfx->data, 6); lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR); wfx->audiostream = flags&0x7f; } else if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) { /* wma pro decoder needs the extra-data */ read(fd, wfx->data, wfx->datalen); lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); wfx->audiostream = flags&0x7f; /* Correct codectype to redirect playback to the proper .codec */ id3->codectype = AFMT_WMAPRO; } else { DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); lseek(fd,current.size - 24 - 72,SEEK_CUR); } } } else if (asf_guid_match(¤t.guid, &asf_guid_content_description)) { /* Object contains five 16-bit string lengths, followed by the five strings: title, artist, copyright, description, rating */ uint16_t strlength[5]; int i; //DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24)); /* Read the 5 string lengths - number of bytes included trailing zero */ for (i=0; i<5; i++) { read_uint16le(fd, &strlength[i]); //DEBUGF("strlength = %u\n",strlength[i]); } if (strlength[0] > 0) { /* 0 - Title */ id3->title = id3buf; asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining); } if (strlength[1] > 0) { /* 1 - Artist */ id3->artist = id3buf; asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining); } lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */ if (strlength[3] > 0) { /* 3 - description */ id3->comment = id3buf; asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining); } lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */ } else if (asf_guid_match(¤t.guid, &asf_guid_extended_content_description)) { uint16_t count; int i; int bytesleft = current.size - 24; //DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n"); read_uint16le(fd, &count); bytesleft -= 2; //DEBUGF("extended metadata count = %u\n",count); for (i=0; i < count; i++) { uint16_t length, type; unsigned char* utf8 = utf8buf; int utf8length = 512; read_uint16le(fd, &length); asf_utf16LEdecode(fd, length, &utf8, &utf8length); bytesleft -= 2 + length; read_uint16le(fd, &type); read_uint16le(fd, &length); if (!strcmp("WM/TrackNumber",utf8buf)) { if (type == 0) { id3->track_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); id3->tracknum = atoi(id3->track_string); } else if ((type >=2) && (type <= 5)) { id3->tracknum = asf_intdecode(fd, type, length); } else { lseek(fd, length, SEEK_CUR); } } else if ((!strcmp("WM/Genre", utf8buf)) && (type == 0)) { id3->genre_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/AlbumTitle", utf8buf)) && (type == 0)) { id3->album = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/AlbumArtist", utf8buf)) && (type == 0)) { id3->albumartist = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if ((!strcmp("WM/Composer", utf8buf)) && (type == 0)) { id3->composer = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else if (!strcmp("WM/Year", utf8buf)) { if (type == 0) { id3->year_string = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); id3->year = atoi(id3->year_string); } else if ((type >=2) && (type <= 5)) { id3->year = asf_intdecode(fd, type, length); } else { lseek(fd, length, SEEK_CUR); } } else if (!strncmp("replaygain_", utf8buf, 11)) { char* value = id3buf; int buf_len = id3buf_remaining; int len; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); len = parse_replaygain(utf8buf, value, id3, value, buf_len); if (len == 0) { /* Don't need to keep the value */ id3buf = value; id3buf_remaining = buf_len; } } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { id3->mb_track_id = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); } else { lseek(fd, length, SEEK_CUR); } bytesleft -= 4 + length; } lseek(fd, bytesleft, SEEK_CUR); } else if (asf_guid_match(¤t.guid, &asf_guid_content_encryption) || asf_guid_match(¤t.guid, &asf_guid_extended_content_encryption)) { //DEBUGF("File is encrypted\n"); return ASF_ERROR_ENCRYPTED; } else { //DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24)); lseek(fd,current.size - 24,SEEK_CUR); } //DEBUGF("Parsed object - size = %d\n",(int)current.size); datalen -= current.size; } if (i != (int)subobjects || datalen != 0) { //DEBUGF("header data doesn't match given subobject count\n"); return ASF_ERROR_INVALID_VALUE; } //DEBUGF("%d subobjects read successfully\n", i); } #if 0 tmp = asf_parse_header_validate(file, &header); if (tmp < 0) { /* header read ok but doesn't validate correctly */ return tmp; } #endif //DEBUGF("header validated correctly\n"); return 0; }