bool ID3_TagHeader::Parse(ID3_Reader& reader) { io::ExitTrigger et(reader); if (!ID3_Tag::IsV2Tag(reader)) { ID3D_NOTICE( "ID3_TagHeader::Parse(): not an id3v2 header" ); return false; } uchar id[3]; reader.readChars(id, 3); // The spec version is determined with the MAJOR and MINOR OFFSETs uchar major = reader.readChar(); uchar minor = reader.readChar(); this->SetSpec(ID3_VerRevToV2Spec(major, minor)); // Get the flags at the appropriate offset _flags.set(static_cast<ID3_Flags::TYPE>(reader.readChar())); // set the data size this->SetDataSize(io::readUInt28(reader)); if (_flags.test(HEADER_FLAG_EXTENDED) && this->GetSpec() == ID3V2_2_1) { //couldn't find anything about this in the draft specifying 2.2.1 -> http://www.id3.org/pipermail/id3v2/2000-April/000126.html _flags.set(HEADER_FLAG_EXTENDED, false); _info->extended_bytes = 0; // rest is checked at ParseExtended() } et.setExitPos(reader.getCur()); return true; }
bool lyr3::v1::parse(ID3_TagImpl& tag, ID3_Reader& reader) { io::ExitTrigger et(reader); ID3_Reader::pos_type end = reader.getCur(); if (end < reader.getBeg() + 9 + 128) { ID3D_NOTICE( "id3::v1::parse: bailing, not enough bytes to parse, pos = " << end ); return false; } reader.setCur(end - (9 + 128)); { if (io::readText(reader, 9) != "LYRICSEND" || io::readText(reader, 3) != "TAG") { return false; } } // we have a Lyrics3 v1.00 tag if (end < reader.getBeg() + 11 + 9 + 128) { // the file size isn't large enough to actually hold lyrics ID3D_WARNING( "id3::v1::parse: not enough data to parse lyrics3" ); return false; } // reserve enough space for lyrics3 + id3v1 tag size_t window = end - reader.getBeg(); size_t lyrDataSize = min<size_t>(window, 11 + 5100 + 9 + 128); reader.setCur(end - lyrDataSize); io::WindowedReader wr(reader, lyrDataSize - (9 + 128)); if (!findText(wr, "LYRICSBEGIN")) { ID3D_WARNING( "id3::v1::parse: couldn't find LYRICSBEGIN, bailing" ); return false; } et.setExitPos(wr.getCur()); wr.skipChars(11); wr.setBeg(wr.getCur()); io::LineFeedReader lfr(wr); String lyrics = io::readText(lfr, wr.remainingBytes()); id3::v2::setLyrics(tag, lyrics, "Converted from Lyrics3 v1.00", "XXX"); return true; }
bool ID3_FieldImpl::ParseText(ID3_Reader& reader) { ID3D_NOTICE( "ID3_Field::ParseText(): reader.getBeg() = " << reader.getBeg() ); ID3D_NOTICE( "ID3_Field::ParseText(): reader.getCur() = " << reader.getCur() ); ID3D_NOTICE( "ID3_Field::ParseText(): reader.getEnd() = " << reader.getEnd() ); this->Clear(); ID3_TextEnc enc = this->GetEncoding(); size_t fixed_size = this->Size(); if (fixed_size) { ID3D_NOTICE( "ID3_Field::ParseText(): fixed size string" ); // The string is of fixed length String text = readEncodedText(reader, fixed_size, enc); this->SetText(text); ID3D_NOTICE( "ID3_Field::ParseText(): fixed size string = " << text ); } else if (_flags & ID3FF_LIST) { ID3D_NOTICE( "ID3_Field::ParseText(): text list" ); // lists are always the last field in a frame. parse all remaining // characters in the reader while (!reader.atEnd()) { String text = readEncodedString(reader, enc); this->AddText(text); ID3D_NOTICE( "ID3_Field::ParseText(): adding string = " << text ); } } else if (_flags & ID3FF_CSTR) { ID3D_NOTICE( "ID3_Field::ParseText(): null terminated string" ); String text = readEncodedString(reader, enc); this->SetText(text); ID3D_NOTICE( "ID3_Field::ParseText(): null terminated string = " << text ); } else { ID3D_NOTICE( "ID3_Field::ParseText(): last field string" ); String text = readEncodedText(reader, reader.remainingBytes(), enc); // not null terminated. this->AddText(text); ID3D_NOTICE( "ID3_Field::ParseText(): last field string = " << text ); } _changed = false; return true; }
bool ID3_FieldImpl::ParseInteger(ID3_Reader& reader) { ID3D_NOTICE( "ID3_FieldImpl::ParseInteger(): beg = " << reader.getBeg() ); ID3D_NOTICE( "ID3_FieldImpl::ParseInteger(): cur = " << reader.getCur() ); ID3D_NOTICE( "ID3_FieldImpl::ParseInteger(): end = " << reader.getEnd() ); bool success = false; if (!reader.atEnd()) { this->Clear(); size_t fixed = this->Size(); size_t nBytes = (fixed > 0) ? fixed : sizeof(uint32); this->Set(io::readBENumber(reader, nBytes)); _changed = false; success = true; } return success; }
bool ID3_TagHeader::Parse(ID3_Reader& reader) { io::ExitTrigger et(reader); if (!ID3_Tag::IsV2Tag(reader)) { ID3D_NOTICE( "ID3_TagHeader::Parse(): not an id3v2 header" ); return false; } uchar id[3]; reader.readChars(id, 3); // The spec version is determined with the MAJOR and MINOR OFFSETs uchar major = reader.readChar(); uchar minor = reader.readChar(); this->SetSpec(ID3_VerRevToV2Spec(major, minor)); // Get the flags at the appropriate offset _flags.set(static_cast<ID3_Flags::TYPE>(reader.readChar())); // set the data size this->SetDataSize(io::readUInt28(reader)); if (_flags.test(EXTENDED)) { if (this->GetSpec() == ID3V2_2_1) { // okay, if we are ID3v2.2.1, then let's skip over the extended header // for now because I am lazy } if (this->GetSpec() == ID3V2_3_0) { // okay, if we are ID3v2.3.0, then let's actually parse the extended // header (for now, we skip it because we are lazy) } } et.setExitPos(reader.getCur()); return true; }
bool ID3_FrameHeader::Parse(ID3_Reader& reader) { ID3D_NOTICE( "ID3_FrameHeader::Parse(): getCur() = " << reader.getCur() ); io::ExitTrigger et(reader); if (!_info) { return false; } if (reader.getEnd() < reader.getCur() + 10) { return false; } String textID = io::readText(reader, _info->frame_bytes_id); ID3D_NOTICE( "ID3_FrameHeader::Parse: textID = " << textID ); ID3D_NOTICE( "ID3_FrameHeader::Parse: getCur() = " << reader.getCur() ); ID3_FrameID fid = ID3_FindFrameID(textID.c_str()); if (ID3FID_NOFRAME == fid) { this->SetUnknownFrame(textID.c_str()); ID3D_NOTICE( "ID3_FrameHeader::Parse: unknown frame id" ); } else { this->SetFrameID(fid); } uint32 dataSize = io::readBENumber(reader, _info->frame_bytes_size); ID3D_NOTICE( "ID3_FrameHeader::Parse: dataSize = " << dataSize ); ID3D_NOTICE( "ID3_FrameHeader::Parse: getCur() = " << reader.getCur() ); this->SetDataSize(dataSize); uint32 flags = io::readBENumber(reader, _info->frame_bytes_flags); _flags.add(flags); ID3D_NOTICE( "ID3_FrameHeader::Parse: flags = " << flags ); ID3D_NOTICE( "ID3_FrameHeader::Parse: getCur() = " << reader.getCur() ); et.setExitPos(reader.getCur()); return true; }
bool id3::v1::parse(ID3_TagImpl& tag, ID3_Reader& reader) { io::ExitTrigger et(reader); ID3_Reader::pos_type end = reader.getCur(); // posn ourselves at 128 bytes from the current position if (end < reader.getBeg() + ID3_V1_LEN) { ID3D_NOTICE( "id3::v1::parse: not enough bytes to parse, pos = " << end ); return false; } reader.setCur(end - ID3_V1_LEN); ID3_Reader::pos_type beg = reader.getCur(); //file.seekg(-static_cast<long>(ID3_V1_LEN), ios::cur); if (end != beg + ID3_V1_LEN) { ID3D_WARNING( "id3::v1::parse: failed to reposition " << ID3_V1_LEN << " bytes" ); return false; } // read the next 128 bytes in; String field = io::readText(reader, ID3_V1_LEN_ID); // check to see if it was a tag if (field != "TAG") { return false; } et.setExitPos(beg); // guess so, let's start checking the v2 tag for frames which are the // equivalent of the v1 fields. When we come across a v1 field that has // no current equivalent v2 frame, we create the frame, copy the data // from the v1 frame and attach it to the tag // (Scott Wheeler) The above comment was nice in theory, but it wasn't // first checking (before my hacks) to see if there already was v2 data. ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String title = io::readTrailingSpaces(reader, ID3_V1_LEN_TITLE); field = id3::v2::getTitle(tag); if (title.size() > 0 && (field.size() == 0 || field == "")) { id3::v2::setTitle(tag, title); } ID3D_NOTICE( "id3::v1::parse: title = \"" << title << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String artist = io::readTrailingSpaces(reader, ID3_V1_LEN_ARTIST); field = id3::v2::getArtist(tag); if (artist.size() > 0 && (field.size() == 0 || field == "")) { id3::v2::setArtist(tag, artist); } ID3D_NOTICE( "id3::v1::parse: artist = \"" << artist << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String album = io::readTrailingSpaces(reader, ID3_V1_LEN_ALBUM); field = id3::v2::getAlbum(tag); if (album.size() > 0 && (field.size() == 0 || field == "")) { id3::v2::setAlbum(tag, album); } ID3D_NOTICE( "id3::v1::parse: album = \"" << title << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String year = io::readTrailingSpaces(reader, ID3_V1_LEN_YEAR); field = id3::v2::getYear(tag); if (year.size() > 0 && (field.size() == 0 || field == "")) { id3::v2::setYear(tag, year); } ID3D_NOTICE( "id3::v1::parse: year = \"" << year << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String comment = io::readTrailingSpaces(reader, ID3_V1_LEN_COMMENT-2); // fixes bug for when tracknumber is 0x20 BString trackno = io::readBinary(reader, ID3_V1_LEN_COMMENT-28); if (trackno[0] == '\0') { if (trackno[1] != '\0') { //we've got a tracknumber size_t track = trackno[1]; field = id3::v2::getTrack(tag); if (field.size() == 0 || field == "00") { id3::v2::setTrack(tag, track, 0); } ID3D_NOTICE( "id3::v1::parse: track = \"" << track << "\"" ); ID3D_NOTICE( "id3::v1::parse: comment length = \"" << comment.length() << "\"" ); } } else { // trackno[0] != '\0' const int paddingsize = (ID3_V1_LEN_COMMENT-2) - comment.size(); const char * padding = " "; //28 spaces if (trackno[1] == '\0' || trackno[1] == 0x20 && trackno[0] != 0x20) { // if there used to be spaces they are gone now, we need to rebuild them comment.append(padding, paddingsize); comment.append((const char *)trackno.data(), 1); } else if (trackno[1] != '\0' && trackno[1] != 0x20 && trackno[0] != 0x20) { // if there used to be spaces they are gone now, we need to rebuild them comment.append(padding, paddingsize); comment.append((const char *)trackno.data(), 2); } } ID3D_NOTICE( "id3::v1::parse: comment = \"" << comment << "\"" ); if (comment.size() > 0) { id3::v2::setComment(tag, comment, STR_V1_COMMENT_DESC, "XXX"); } ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); // the GENRE field/frame uchar genre = reader.readChar(); field = id3::v2::getGenre(tag); if (genre != 0xFF && (field.size() == 0 || field == "")) { id3::v2::setGenre(tag, genre); } ID3D_NOTICE( "id3::v1::parse: genre = \"" << (int) genre << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); return true; }
bool mm::parse(ID3_TagImpl& tag, ID3_Reader& rdr) { io::ExitTrigger et(rdr); ID3_Reader::pos_type end = rdr.getCur(); if (end < rdr.getBeg() + 48) { ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse, pos = " << end ); return false; } rdr.setCur(end - 48); String version; { if (io::readText(rdr, 32) != "Brava Software Inc. ") { ID3D_NOTICE( "mm::parse: bailing, couldn't find footer" ); return false; } version = io::readText(rdr, 4); if (version.size() != 4 || !isdigit(version[0]) || version[1] != '.' || !isdigit(version[2]) || !isdigit(version[3])) { ID3D_WARNING( "mm::parse: bailing, nonstandard version = " << version ); return false; } } ID3_Reader::pos_type beg = rdr.setCur(end - 48); et.setExitPos(beg); if (end < 68) { ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse offsets, pos = " << end ); return false; } rdr.setCur(end - 68); io::WindowedReader dataWindow(rdr); dataWindow.setEnd(rdr.getCur()); uint32 offsets[5]; io::WindowedReader offsetWindow(rdr, 20); for (size_t i = 0; i < 5; ++i) { offsets[i] = io::readLENumber(rdr, sizeof(uint32)); } size_t metadataSize = 0; if (version <= "3.00") { // All MusicMatch tags up to and including version 3.0 had metadata // sections exactly 7868 bytes in length. metadataSize = 7868; } else { // MusicMatch tags after version 3.0 had three possible lengths for their // metadata sections. We can determine which it was by searching for // the version section signature that should precede the metadata section // by exactly 256 bytes. size_t possibleSizes[] = { 8132, 8004, 7936 }; for (size_t i = 0; i < sizeof(possibleSizes)/sizeof(size_t); ++i) { dataWindow.setCur(dataWindow.getEnd()); // Our offset will be exactly 256 bytes prior to our potential metadata // section size_t offset = possibleSizes[i] + 256; if (dataWindow.getCur() < offset) { // if our filesize is less than the offset, then it can't possibly // be the correct offset, so try again. continue; } dataWindow.setCur(dataWindow.getCur() - offset); // now read in the signature to see if it's a match if (io::readText(dataWindow, 8) == "18273645") { metadataSize = possibleSizes[i]; break; } } } if (0 == metadataSize) { // if we didn't establish a size for the metadata, then something is // wrong. probably should log this. ID3D_WARNING( "mm::parse: bailing, couldn't find meta data signature, end = " << end ); return false; } // parse the offset pointers to determine the actual sizes of all the // sections size_t sectionSizes[5]; size_t tagSize = metadataSize; // we already know the size of the last section sectionSizes[4] = metadataSize; size_t lastOffset = 0; for (int i = 0; i < 5; i++) { size_t thisOffset = offsets[i]; //ASSERT(thisOffset > lastOffset); if (i > 0) { size_t sectionSize = thisOffset - lastOffset; sectionSizes[i-1] = sectionSize; tagSize += sectionSize; } lastOffset = thisOffset; } // now check to see that our tag size is reasonable if (dataWindow.getEnd() < tagSize) { // Ack! The tag size doesn't jive with the tag's ending position in // the file. Bail! ID3D_WARNING( "mm::parse: bailing, tag size is too big, tag size = " << tagSize << ", end = " << end ); return false; } dataWindow.setBeg(dataWindow.getEnd() - tagSize); dataWindow.setCur(dataWindow.getBeg()); // Now calculate the adjusted offsets offsets[0] = dataWindow.getBeg(); for (size_t i = 0; i < 4; ++i) { offsets[i+1] = offsets[i] + sectionSizes[i]; } // now check for a tag header and adjust the tag_beg pointer appropriately if (dataWindow.getBeg() >= 256) { rdr.setCur(dataWindow.getBeg() - 256); if (io::readText(rdr, 8) == "18273645") { et.setExitPos(rdr.getCur() - 8); } else { et.setExitPos(dataWindow.getBeg()); } dataWindow.setCur(dataWindow.getBeg()); } // Now parse the various sections... // Parse the image extension at offset 0 dataWindow.setCur(offsets[0]); String imgExt = io::readTrailingSpaces(dataWindow, 4); // Parse the image binary at offset 1 dataWindow.setCur(offsets[1]); uint32 imgSize = io::readLENumber(dataWindow, 4); if (imgSize == 0) { // no image binary. don't do anything. } else { io::WindowedReader imgWindow(dataWindow, imgSize); if (imgWindow.getEnd() < imgWindow.getBeg() + imgSize) { // Ack! The image size given extends beyond the next offset! This is // not good... log? } else { BString imgData = io::readAllBinary(imgWindow); ID3_Frame* frame = new ID3_Frame(ID3FID_PICTURE); if (frame) { String mimetype("image/"); mimetype += imgExt; frame->GetField(ID3FN_MIMETYPE)->Set(mimetype.c_str()); frame->GetField(ID3FN_IMAGEFORMAT)->Set(""); frame->GetField(ID3FN_PICTURETYPE)->Set(static_cast<unsigned int>(0)); frame->GetField(ID3FN_DESCRIPTION)->Set(""); frame->GetField(ID3FN_DATA)->Set(reinterpret_cast<const uchar*>(imgData.data()), imgData.size()); tag.AttachFrame(frame); } } } //file.seekg(offsets[2]); //file.seekg(offsets[3]); dataWindow.setCur(offsets[4]); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_TITLE)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_ALBUM)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_LEADARTIST)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_CONTENTTYPE)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Tempo")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Mood")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Situation")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Preference")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_SONGLEN)); // The next 12 bytes can be ignored. The first 8 represent the // creation date as a 64 bit floating point number. The last 4 are // for a play counter. dataWindow.skipChars(12); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Path")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Serial")); // 2 bytes for track uint32 trkNum = io::readLENumber(dataWindow, 2); if (trkNum > 0) { String trkStr = toString(trkNum); ID3_Frame* frame = new ID3_Frame(ID3FID_TRACKNUM); if (frame) { frame->GetField(ID3FN_TEXT)->Set(trkStr.c_str()); tag.AttachFrame(frame); } } tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Notes")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Bio")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_UNSYNCEDLYRICS)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWARTIST)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWCOMMERCIALINFO)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_ArtistEmail")); // email? return true; }
//bool parse(TagImpl& tag, ID3_Reader& reader) bool lyr3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader) { io::ExitTrigger et(reader); ID3_Reader::pos_type end = reader.getCur(); if (end < reader.getBeg() + 6 + 9 + 128) { ID3D_NOTICE( "lyr3::v2::parse: bailing, not enough bytes to parse, pos = " << reader.getCur() ); return false; } reader.setCur(end - (6 + 9 + 128)); uint32 lyrSize = 0; ID3_Reader::pos_type beg = reader.getCur(); lyrSize = readIntegerString(reader, 6); if (reader.getCur() < beg + 6) { ID3D_NOTICE( "lyr3::v2::parse: couldn't find numeric string, lyrSize = " << lyrSize ); return false; } if (io::readText(reader, 9) != "LYRICS200" || io::readText(reader, 3) != "TAG") { return false; } if (end < reader.getBeg() + lyrSize + 6 + 9 + 128) { ID3D_WARNING( "lyr3::v2::parse: not enough data to parse tag, lyrSize = " << lyrSize ); return false; } reader.setCur(end - (lyrSize + 6 + 9 + 128)); io::WindowedReader wr(reader); wr.setWindow(wr.getCur(), lyrSize); beg = wr.getCur(); if (io::readText(wr, 11) != "LYRICSBEGIN") { // not a lyrics v2.00 tag ID3D_WARNING( "lyr3::v2::parse: couldn't find LYRICSBEGIN, bailing" ); return false; } bool has_time_stamps = false; ID3_Frame* lyr_frame = NULL; while (!wr.atEnd()) { uint32 fldSize; String fldName = io::readText(wr, 3); ID3D_NOTICE( "lyr3::v2::parse: fldName = " << fldName ); fldSize = readIntegerString(wr, 5); ID3D_NOTICE( "lyr3::v2::parse: fldSize = " << fldSize ); String fldData; io::WindowedReader wr2(wr, fldSize); io::LineFeedReader lfr(wr2); fldData = io::readText(lfr, fldSize); ID3D_NOTICE( "lyr3::v2::parse: fldData = \"" << fldData << "\"" ); // the IND field if (fldName == "IND") { has_time_stamps = (fldData.size() > 1 && fldData[1] == '1'); } // the TITLE field else if (fldName == "ETT" && !id3::v2::hasTitle(tag)) { //tag.setTitle(fldData); id3::v2::setTitle(tag, fldData); } // the ARTIST field else if (fldName == "EAR" && !id3::v2::hasArtist(tag)) { //tag.setArtist(fldData); id3::v2::setArtist(tag, fldData); } // the ALBUM field else if (fldName == "EAL" && !id3::v2::hasAlbum(tag)) { //tag.setAlbum(fldData); id3::v2::setAlbum(tag, fldData); } // the Lyrics/Music AUTHOR field else if (fldName == "AUT") { //tag.setAuthor(fldData); id3::v2::setLyricist(tag, fldData); } // the INFORMATION field else if (fldName == "INF") { //tag.setInfo(fldData); id3::v2::setComment(tag, fldData, "Lyrics3 v2.00 INF", "XXX"); } // the LYRICS field else if (fldName == "LYR") { // if already found an INF field, use it as description String desc = "Converted from Lyrics3 v2.00"; //tag.setLyrics(fldData); if (!has_time_stamps) { lyr_frame = id3::v2::setLyrics(tag, fldData, desc, "XXX"); } else { // converts from lyrics3 to SYLT in-place io::StringReader sr(fldData); ID3D_NOTICE( "lyr3::v2::parse: determining synced lyrics" ); BString sylt; io::BStringWriter sw(sylt); lyrics3ToSylt(sr, sw); lyr_frame = id3::v2::setSyncLyrics(tag, sylt, ID3TSF_MS, desc, "XXX", ID3CT_LYRICS); ID3D_NOTICE( "lyr3::v2::parse: determined synced lyrics" ); } } else if (fldName == "IMG") { // currently unsupported ID3D_WARNING( "lyr3::v2::parse: IMG field unsupported" ); } else { ID3D_WARNING( "lyr3::v2::parse: undefined field id: " << fldName ); } } et.setExitPos(beg); return true; }
void ID3_TagHeader::ParseExtended(ID3_Reader& reader) { if (this->GetSpec() == ID3V2_3_0) { /* Extended header size $xx xx xx xx Extended Flags $xx xx Size of padding $xx xx xx xx */ // skip over header size, we are not using it anyway, we calculate it reader.setCur(reader.getCur()+4); //Extended header size //io::readBENumber(reader, 4); //Extended header size uint16 tmpval = io::readBENumber(reader, 2); //Extended Flags // skip over padding size, we are not using it anyway reader.setCur(reader.getCur()+4); //Size of padding // io::readBENumber(reader, 4); //Size of padding if (tmpval != 0) //there is only one flag defined in ID3V2_3_0: crc { //skip over crc data, we are not using it anyway reader.setCur(reader.getCur()+4); //Crc //io::readBENumber(reader, 4); //Crc _info->extended_bytes = 14; } else _info->extended_bytes = 10; } if (this->GetSpec() == ID3V2_4_0) { /* Extended header size 4 * %0xxxxxxx Number of flag bytes $01 Extended Flags $xx */ uint16 i; uint16 extrabytes; io::readUInt28(reader); const int extflagbytes = reader.readChar(); //Number of flag bytes ID3_Flags* extflags[1]; // ID3V2_4_0 has 1 flag byte, extflagbytes should be equal to 1 for (i = 0; i < extflagbytes; ++i) { extflags[i] = new ID3_Flags; extflags[i]->set(reader.readChar()); //flags } extrabytes = 0; //extflags[0]->test(EXT_HEADER_FLAG_BIT1); // ID3V2_4_0 ext header flag bit 1 *should* be 0 if (extflags[0]->test(EXT_HEADER_FLAG_BIT2)) { // ID3V2_4_0 ext header flag bit 2 = Tag is an update // read size extrabytes += 1; // add a byte for the char containing the extflagdatasize const int extheaderflagdatasize = reader.readChar(); extrabytes += extheaderflagdatasize; // Set the cursor right; we are not parsing the data, no-one is using extended flags anyway reader.setCur(reader.getCur() + extheaderflagdatasize); //reader.readChars(buf, extheaderflagdatasize); //buf should be at least 127 bytes = max extended header flagdata size } if (extflags[0]->test(EXT_HEADER_FLAG_BIT3)) { // ID3V2_4_0 ext header flag bit 3 = CRC data present // read size extrabytes += 1; // add a byte for the char containing the extflagdatasize const int extheaderflagdatasize = reader.readChar(); extrabytes += extheaderflagdatasize; // Set the cursor right; we are not parsing the data, no-one is using extended flags anyway reader.setCur(reader.getCur() + extheaderflagdatasize); //reader.readChars(buf, extheaderflagdatasize); //buf should be at least 127 bytes = max extended header flagdata size } if (extflags[0]->test(EXT_HEADER_FLAG_BIT4)) { // ID3V2_4_0 ext header flag bit 4 = Tag restrictions // read size extrabytes += 1; // add a byte for the char containing the extflagdatasize const int extheaderflagdatasize = reader.readChar(); extrabytes += extheaderflagdatasize; // Set the cursor right; we are not parsing the data, no-one is using extended flags anyway reader.setCur(reader.getCur() + extheaderflagdatasize); //reader.readChars(buf, extheaderflagdatasize); //buf should be at least 127 bytes = max extended header flagdata size } _info->extended_bytes = 5 + extflagbytes + extrabytes; } // a bit unorthodox, but since we are not using any of the extended header, but were merely // parsing it to get the cursor right, we delete it. Be Gone ! _flags.set(HEADER_FLAG_EXTENDED, false); if (_info) { _data_size -= _info->extended_bytes; _info->extended_bytes = 0; }//else there is a tag with a higher or lower version than supported }
bool ID3_FrameImpl::Parse(ID3_Reader& reader) { io::ExitTrigger et(reader); ID3D_NOTICE( "ID3_FrameImpl::Parse(): reader.getBeg() = " << reader.getBeg() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): reader.getCur() = " << reader.getCur() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): reader.getEnd() = " << reader.getEnd() ); ID3_Reader::pos_type beg = reader.getCur(); if (!_hdr.Parse(reader) || reader.getCur() == beg) { ID3D_WARNING( "ID3_FrameImpl::Parse(): no header to parse" ); return false; } ID3D_NOTICE( "ID3_FrameImpl::Parse(): after hdr, getCur() = " << reader.getCur() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): found frame! id = " << _hdr.GetTextID() ); // data is the part of the frame buffer that appears after the header const size_t dataSize = _hdr.GetDataSize(); ID3D_NOTICE( "ID3_FrameImpl::Parse(): dataSize = " << dataSize ); if (reader.getEnd() < beg + dataSize) { ID3D_WARNING( "ID3_FrameImpl::Parse(): not enough data to parse frame" ); return false; } if (dataSize > 16777216) //Klenotic: The max frame size is 16MB according to http://www.id3.org/easy.html. A corrupted tag that reports a frame size of (-1) will crash the program. { ID3D_WARNING( "ID3_FrameImpl::Parse(): frame size too large" ); return false; } io::WindowedReader wr(reader, dataSize); ID3D_NOTICE( "ID3_FrameImpl::Parse(): window getBeg() = " << wr.getBeg() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): window getCur() = " << wr.getCur() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): window getEnd() = " << wr.getEnd() ); unsigned long origSize = 0; if (_hdr.GetCompression()) { origSize = io::readBENumber(reader, sizeof(uint32)); // allocate 2MB instead 4GB max in the decompressor later on (TODO decompressor should actually do the sanitycheck) if (origSize > 2 * 1024 * 1024){ ID3D_WARNING( "ID3_FrameImpl::Parse(): _hdr.GetCompression() exeeds sanity limit" ); return false; } ID3D_NOTICE( "ID3_FrameImpl::Parse(): frame is compressed, origSize = " << origSize ); } if (_hdr.GetEncryption()) { char ch = static_cast<char>(wr.readChar()); this->SetEncryptionID(ch); ID3D_NOTICE( "ID3_FrameImpl::Parse(): frame is encrypted, encryption_id = " << (int) ch ); } if (_hdr.GetGrouping()) { char ch = static_cast<char>(wr.readChar()); this->SetGroupingID(ch); ID3D_NOTICE( "ID3_FrameImpl::Parse(): frame is encrypted, grouping_id = " << (int) ch ); } // set the type of frame based on the parsed header this->_ClearFields(); this->_InitFields(); bool success = false; // expand out the data if it's compressed if (!_hdr.GetCompression()) { success = parseFields(wr, *this); } else { io::CompressedReader csr(wr, origSize); success = parseFields(csr, *this); } et.setExitPos(wr.getCur()); _changed = false; return true; }
bool id3::v1::parse(ID3_TagImpl& tag, ID3_Reader& reader) { io::ExitTrigger et(reader); ID3_Reader::pos_type end = reader.getCur(); // posn ourselves at 128 bytes from the current position if (end < reader.getBeg() + ID3_V1_LEN) { ID3D_NOTICE( "id3::v1::parse: not enough bytes to parse, pos = " << end ); return false; } reader.setCur(end - ID3_V1_LEN); ID3_Reader::pos_type beg = reader.getCur(); //file.seekg(-static_cast<long>(ID3_V1_LEN), ios::cur); if (end != beg + ID3_V1_LEN) { ID3D_WARNING( "id3::v1::parse: failed to reposition " << ID3_V1_LEN << " bytes" ); return false; } // read the next 128 bytes in; String id = io::readText(reader, ID3_V1_LEN_ID); // check to see if it was a tag if (id != "TAG") { return false; } et.setExitPos(beg); // guess so, let's start checking the v2 tag for frames which are the // equivalent of the v1 fields. When we come across a v1 field that has // no current equivalent v2 frame, we create the frame, copy the data // from the v1 frame and attach it to the tag ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String title = io::readTrailingSpaces(reader, ID3_V1_LEN_TITLE); if (title.size() > 0) { id3::v2::setTitle(tag, title); } ID3D_NOTICE( "id3::v1::parse: title = \"" << title << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String artist = io::readTrailingSpaces(reader, ID3_V1_LEN_ARTIST); if (artist.size() > 0) { id3::v2::setArtist(tag, artist); } ID3D_NOTICE( "id3::v1::parse: artist = \"" << artist << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String album = io::readTrailingSpaces(reader, ID3_V1_LEN_ALBUM); if (album.size() > 0) { id3::v2::setAlbum(tag, album); } ID3D_NOTICE( "id3::v1::parse: album = \"" << title << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String year = io::readTrailingSpaces(reader, ID3_V1_LEN_YEAR); if (year.size() > 0) { id3::v2::setYear(tag, year); } ID3D_NOTICE( "id3::v1::parse: year = \"" << year << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); String comment = io::readTrailingSpaces(reader, ID3_V1_LEN_COMMENT); if (comment.length() == ID3_V1_LEN_COMMENT && '\0' == comment[ID3_V1_LEN_COMMENT - 2] || '\0' != comment[ID3_V1_LEN_COMMENT - 1]) { // This is an id3v1.1 tag. The last byte of the comment is the track // number. size_t track = comment[ID3_V1_LEN_COMMENT - 1]; id3::v2::setTrack(tag, track, 0); ID3D_NOTICE( "id3::v1::parse: track = \"" << track << "\"" ); ID3D_NOTICE( "id3::v1::parse: comment length = \"" << comment.length() << "\"" ); io::StringReader sr(comment); comment = io::readTrailingSpaces(sr, ID3_V1_LEN_COMMENT - 2); } ID3D_NOTICE( "id3::v1::parse: comment = \"" << comment << "\"" ); if (comment.size() > 0) { id3::v2::setComment(tag, comment, STR_V1_COMMENT_DESC, "XXX"); } ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); // the GENRE field/frame uchar genre = reader.readChar(); if (genre != 0xFF) { id3::v2::setGenre(tag, genre); } ID3D_NOTICE( "id3::v1::parse: genre = \"" << (int) genre << "\"" ); ID3D_NOTICE("id3::v1::parse: read bytes: " << reader.getCur() - beg); return true; }
bool id3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader) { ID3_Reader::pos_type beg = reader.getCur(); io::ExitTrigger et(reader); ID3_TagHeader hdr; io::WindowedReader wr(reader, ID3_TagHeader::SIZE); if (!hdr.Parse(wr) || wr.getCur() == beg) { ID3D_NOTICE( "id3::v2::parse(): parsing header failes" ); return false; } if (hdr.GetExtended()) { hdr.ParseExtended(reader); } tag.SetSpec(hdr.GetSpec()); size_t dataSize = hdr.GetDataSize(); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): dataSize = " << dataSize); wr.setWindow(wr.getCur(), dataSize); et.setExitPos(wr.getEnd()); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window beg = " << wr.getBeg() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window cur = " << wr.getCur() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window end = " << wr.getEnd() ); tag.SetExtended(hdr.GetExtended()); if (!hdr.GetUnsync()) { tag.SetUnsync(false); parseFrames(tag, wr); } else { // The buffer has been unsynced. It will have to be resynced to be // readable. This has to be done a character at a time. // // The original reader may be reading in characters from a file. Doing // this a character at a time is quite slow. To improve performance, read // in the entire buffer into a string, then create an UnsyncedReader from // the string. // // It might be better to implement a BufferedReader so that the details // of this can be abstracted away behind a class tag.SetUnsync(true); BString raw = io::readAllBinary(wr); io::BStringReader bsr(raw); io::UnsyncedReader ur(bsr); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync beg = " << ur.getBeg() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync cur = " << ur.getCur() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync end = " << ur.getEnd() ); // Now read the UnsyncedReader into another string, and parse the frames // from the string. This is done so that 1. the unsynced reader is // unsynced exactly once, removing the possibility of multiple unsyncings // of the same string, and 2) so that calls to readChars aren't done a // character at a time for every call BString synced = io::readAllBinary(ur); io::BStringReader sr(synced); parseFrames(tag, sr); } return true; }
bool ID3_FrameImpl::Parse(ID3_Reader& reader) { io::ExitTrigger et(reader); ID3D_NOTICE( "ID3_FrameImpl::Parse(): reader.getBeg() = " << reader.getBeg() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): reader.getCur() = " << reader.getCur() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): reader.getEnd() = " << reader.getEnd() ); ID3_Reader::pos_type beg = reader.getCur(); if (!_hdr.Parse(reader) || reader.getCur() == beg) { ID3D_WARNING( "ID3_FrameImpl::Parse(): no header to parse" ); return false; } ID3D_NOTICE( "ID3_FrameImpl::Parse(): after hdr, getCur() = " << reader.getCur() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): found frame! id = " << _hdr.GetTextID() ); // data is the part of the frame buffer that appears after the header const size_t dataSize = _hdr.GetDataSize(); ID3D_NOTICE( "ID3_FrameImpl::Parse(): dataSize = " << dataSize ); if (reader.getEnd() < beg + dataSize) { ID3D_WARNING( "ID3_FrameImpl::Parse(): not enough data to parse frame" ); return false; } io::WindowedReader wr(reader, dataSize); ID3D_NOTICE( "ID3_FrameImpl::Parse(): window getBeg() = " << wr.getBeg() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): window getCur() = " << wr.getCur() ); ID3D_NOTICE( "ID3_FrameImpl::Parse(): window getEnd() = " << wr.getEnd() ); unsigned long origSize = 0; if (_hdr.GetCompression()) { origSize = io::readBENumber(reader, sizeof(uint32)); ID3D_NOTICE( "ID3_FrameImpl::Parse(): frame is compressed, origSize = " << origSize ); } if (_hdr.GetEncryption()) { char ch = wr.readChar(); this->SetEncryptionID(ch); ID3D_NOTICE( "ID3_FrameImpl::Parse(): frame is encrypted, encryption_id = " << (int) ch ); } if (_hdr.GetGrouping()) { char ch = wr.readChar(); this->SetGroupingID(ch); ID3D_NOTICE( "ID3_FrameImpl::Parse(): frame is encrypted, grouping_id = " << (int) ch ); } // set the type of frame based on the parsed header this->_ClearFields(); this->_InitFields(); bool success = false; // expand out the data if it's compressed if (!_hdr.GetCompression()) { success = parseFields(wr, *this); } else { io::CompressedReader csr(wr, origSize); success = parseFields(csr, *this); } et.setExitPos(wr.getCur()); _changed = false; return true; }