String io::readString(ID3_Reader& reader) { String str; while (!reader.atEnd()) { ID3_Reader::char_type ch = static_cast<ID3_Reader::char_type>(reader.readChar()); if (ch == '\0') { break; } str += static_cast<char>(ch); } return str; }
uint32 io::readUInt28(ID3_Reader& reader) { uint32 val = 0; const unsigned short BITSUSED = 7; const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32)); // For each byte of the first 4 bytes in the string... for (size_t i = 0; i < sizeof(uint32); ++i) { if (reader.atEnd()) { break; } // ...append the last 7 bits to the end of the temp integer... val = (val << BITSUSED) | static_cast<uint32>(reader.readChar()) & MASK(BITSUSED); } // We should always parse 4 characters return min(val, MAXVAL); }
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; }
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::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; }