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_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_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; }
//used for streaming media void ID3_TagImpl::ParseReader(ID3_Reader &reader) { size_t mp3_core_size; size_t bytes_till_sync; io::WindowedReader wr(reader); wr.setBeg(wr.getCur()); _file_tags.clear(); _file_size = reader.getEnd(); ID3_Reader::pos_type beg = wr.getBeg(); ID3_Reader::pos_type cur = wr.getCur(); ID3_Reader::pos_type end = wr.getEnd(); ID3_Reader::pos_type last = cur; if (_tags_to_parse.test(ID3TT_ID3V2)) { do { last = cur; // Parse tags at the beginning of the file first... if (id3::v2::parse(*this, wr)) { _file_tags.add(ID3TT_ID3V2); } cur = wr.getCur(); wr.setBeg(cur); } while (!wr.atEnd() && cur > last); } // add silly padding outside the tag to _prepended_bytes if (!wr.atEnd() && wr.peekChar() == '\0') { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): found padding outside tag" ); do { last = cur; cur = wr.getCur() + 1; wr.setBeg(cur); wr.setCur(cur); } while (!wr.atEnd() && cur > last && wr.peekChar() == '\0'); } if (!wr.atEnd() && _file_size - (cur - beg) > 4 && wr.peekChar() == 255) { //unfortunatly, this is necessary for finding an invalid padding wr.setCur(cur + 1); //cur is known by peekChar if (wr.readChar() == '\0' && wr.readChar() == '\0' && wr.peekChar() == '\0') { //three empty bytes found, enough for me, this is stupid padding cur += 3; //those are now allready read in (excluding the peekChar, since it will be added by do{}) do { last = cur; cur = wr.getCur() + 1; wr.setBeg(cur); wr.setCur(cur); } while (!wr.atEnd() && cur > last && wr.peekChar() == '\0'); } else wr.setCur(cur); } _prepended_bytes = cur - beg; // go looking for the first sync byte to add to bytes_till_sync // by not adding it to _prepended_bytes, we preserve this 'unknown' data // The routine's only effect is helping the lib to find things as bitrate etc. beg = wr.getBeg(); if (!wr.atEnd() && wr.peekChar() != 0xFF) //no sync byte, so, either this is not followed by a mp3 file or it's a fLaC file, or an encapsulating format, better check it { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): Didn't find mp3 sync byte" ); if ((_file_size - (cur - beg)) >= 4) { //there is room to search for some kind of ID unsigned char buf[5]; wr.readChars(buf, 4); buf[4] = '\0'; // check for RIFF (an encapsulating format) ID if (strncmp((char*)buf, "RIFF", 4) == 0 || strncmp((char*)buf, "RIFX", 4) == 0) { // next 4 bytes are RIFF size, skip them cur = wr.getCur() + 4; wr.setCur(cur); // loop until first possible sync byte if (!wr.atEnd() && wr.peekChar() != 0xFF) { do { last = cur; cur = wr.getCur() + 1; wr.setCur(cur); } while (!wr.atEnd() && cur > last && wr.peekChar() != 0xFF); } } else if (strncmp((char*)buf, "fLaC", 4) == 0) { //a FLAC file, no need looking for a sync byte beg = cur; } else { //since we set the cursor 4 bytes ahead for looking for RIFF, RIFX or fLaC, better set it back // but peekChar allready checked the first one, so we add one cur = cur + 1; wr.setCur(cur); //go looking for a sync byte if (!wr.atEnd() && wr.peekChar() != 0xFF) //no sync byte, we have an unknown byte { do { last = cur; cur = wr.getCur() + 1; wr.setCur(cur); } while (!wr.atEnd() && cur > last && wr.peekChar() != 0xFF); } } } //if ((_file_size - (cur - beg)) >= 4) else { //remaining size is smaller than 4 bytes, can't be useful, but leave it for now beg = cur; //file.close(); //return; } } bytes_till_sync = cur - beg; cur = wr.setCur(end); if (_file_size > _prepended_bytes) { do { last = cur; ID3D_NOTICE( "ID3_TagImpl::ParseReader(): beg = " << wr.getBeg() ); ID3D_NOTICE( "ID3_TagImpl::ParseReader(): cur = " << wr.getCur() ); ID3D_NOTICE( "ID3_TagImpl::ParseReader(): end = " << wr.getEnd() ); // ...then the tags at the end ID3D_NOTICE( "ID3_TagImpl::ParseReader(): musicmatch? cur = " << wr.getCur() ); if (_tags_to_parse.test(ID3TT_MUSICMATCH) && mm::parse(*this, wr)) { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): musicmatch! cur = " << wr.getCur() ); _file_tags.add(ID3TT_MUSICMATCH); wr.setEnd(wr.getCur()); } ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v1? cur = " << wr.getCur() ); if (_tags_to_parse.test(ID3TT_LYRICS3) && lyr3::v1::parse(*this, wr)) { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v1! cur = " << wr.getCur() ); _file_tags.add(ID3TT_LYRICS3); wr.setEnd(wr.getCur()); } ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v2? cur = " << wr.getCur() ); if (_tags_to_parse.test(ID3TT_LYRICS3V2) && lyr3::v2::parse(*this, wr)) { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v2! cur = " << wr.getCur() ); _file_tags.add(ID3TT_LYRICS3V2); cur = wr.getCur(); wr.setCur(wr.getEnd());//set to end to seek id3v1 tag //check for id3v1 tag and set End accordingly ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1? cur = " << wr.getCur() ); if (_tags_to_parse.test(ID3TT_ID3V1) && id3::v1::parse(*this, wr)) { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1! cur = " << wr.getCur() ); _file_tags.add(ID3TT_ID3V1); } wr.setCur(cur); wr.setEnd(cur); } ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1? cur = " << wr.getCur() ); if (_tags_to_parse.test(ID3TT_ID3V1) && id3::v1::parse(*this, wr)) { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1! cur = " << wr.getCur() ); wr.setEnd(wr.getCur()); _file_tags.add(ID3TT_ID3V1); } cur = wr.getCur(); } while (cur != last); _appended_bytes = end - cur; // Now get the mp3 header mp3_core_size = (_file_size - _appended_bytes) - (_prepended_bytes + bytes_till_sync); if (mp3_core_size >= 4) { //it has at least the size for a mp3 header (a mp3 header is 4 bytes) wr.setBeg(_prepended_bytes + bytes_till_sync); wr.setCur(_prepended_bytes + bytes_till_sync); wr.setEnd(_file_size - _appended_bytes); _mp3_info = new Mp3Info; ID3D_NOTICE( "ID3_TagImpl::ParseReader(): mp3header? cur = " << wr.getCur() ); if (_mp3_info->Parse(wr, mp3_core_size)) { ID3D_NOTICE( "ID3_TagImpl::ParseReader(): mp3header! cur = " << wr.getCur() ); } else { delete _mp3_info; _mp3_info = NULL; } } } else this->SetPadding(false); //no need to pad an empty file }
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; }