Beispiel #1
0
bool
MP4::Tag::save()
{
  ByteVector data;
  for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
    const String name = i->first;
    if(name.startsWith("----")) {
      data.append(renderFreeForm(name, i->second));
    }
    else if(name == "trkn") {
      data.append(renderIntPair(name.data(String::Latin1), i->second));
    }
    else if(name == "disk") {
      data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second));
    }
    else if(name == "cpil" || name == "pgap" || name == "pcst") {
      data.append(renderBool(name.data(String::Latin1), i->second));
    }
    else if(name == "tmpo") {
      data.append(renderInt(name.data(String::Latin1), i->second));
    }
    else if(name == "covr") {
      data.append(renderCovr(name.data(String::Latin1), i->second));
    }
    else if(name.size() == 4){
      data.append(renderText(name.data(String::Latin1), i->second));
    }
    else {
      debug("MP4: Unknown item name \"" + name + "\"");
    }
  }
  data = renderAtom("ilst", data);

  AtomList path = d->atoms->path("moov", "udta", "meta", "ilst");
  if(path.size() == 4) {
    saveExisting(data, path);
  }
  else {
    saveNew(data);
  }

  return true;
}
ByteVector UnsynchronizedLyricsFrame::renderFields() const
{
  ByteVector v;

  v.append(char(d->textEncoding));
  v.append(d->language.size() == 3 ? d->language : "XXX");
  v.append(d->description.data(d->textEncoding));
  v.append(textDelimiter(d->textEncoding));
  v.append(d->text.data(d->textEncoding));

  return v;
}
void Ogg::XiphComment::parse(const ByteVector &data)
{
  // The first thing in the comment data is the vendor ID length, followed by a
  // UTF8 string with the vendor ID.

  uint pos = 0;

  const uint vendorLength = data.toUInt(0, false);
  pos += 4;

  d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
  pos += vendorLength;

  // Next the number of fields in the comment vector.

  const uint commentFields = data.toUInt(pos, false);
  pos += 4;

  if(commentFields > (data.size() - 8) / 4) {
    return;
  }

  for(uint i = 0; i < commentFields; i++) {

    // Each comment field is in the format "KEY=value" in a UTF8 string and has
    // 4 bytes before the text starts that gives the length.

    const uint commentLength = data.toUInt(pos, false);
    pos += 4;

    String comment = String(data.mid(pos, commentLength), String::UTF8);
    pos += commentLength;
    if(pos > data.size()) {
      break;
    }

    int commentSeparatorPosition = comment.find("=");
    if(commentSeparatorPosition == -1) {
      break;
    }

    String key = comment.substr(0, commentSeparatorPosition);
    String value = comment.substr(commentSeparatorPosition + 1);

    addField(key, value, false);
  }
}
Beispiel #4
0
void ID3v2::Tag::setAlbumArt(const ByteVector &v, ID3v2::AttachedPictureFrame::Type arttype, String &mimetype)
{
	if (v.isEmpty()) {
		removeFrames("APIC");
		return;
	} else {
		if (!d->frameListMap["APIC"].isEmpty()) {
			removeFrames("APIC");
		} else {
			// do nothing
		}
		AttachedPictureFrame *f = new AttachedPictureFrame("APIC");
		f->setMimeType(mimetype);
		f->setType(arttype);
		f->setPicture(v);
		addFrame(f);
	}
	return;
}
Beispiel #5
0
bool FileStream::writeBlock(const ByteVector &data)
{
  if(!isOpen()) {
    debug("FileStream::writeBlock() -- invalid file.");
    return false;
  }

  if(readOnly()) {
    debug("FileStream::writeBlock() -- read only file.");
    return false;
  }

  size_t nbBytes = writeFile(d->file, data);
  
  if (nbBytes != data.size()) {
	  debug("FileStream::writeBlock() error, 0 bytes written.");
	  return false;
  }
  
  return true;
}
ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &pattern,
                                     int byteAlign, int max)
{
  ByteVectorList l;

  uint previousOffset = 0;
  for(int offset = v.find(pattern, 0, byteAlign);
      offset != -1 && (max == 0 || max > int(l.size()) + 1);
      offset = v.find(pattern, offset + pattern.size(), byteAlign))
  {
    if(offset - previousOffset > 1)
    l.append(v.mid(previousOffset, offset - previousOffset));
    else
      l.append(ByteVector::null);

    previousOffset = offset + pattern.size();
  }

  if(previousOffset < v.size())
    l.append(v.mid(previousOffset, v.size() - previousOffset));

  return l;
}
ByteVectorList ByteVectorList::split(
  const ByteVector &v, const ByteVector &pattern, size_t byteAlign, size_t max)
{
  ByteVectorList l;

  size_t previousOffset = 0;
  for(size_t offset = v.find(pattern, 0, byteAlign);
      offset != ByteVector::npos() && (max == 0 || max > l.size() + 1);
      offset = v.find(pattern, offset + pattern.size(), byteAlign))
  {
    if(offset - previousOffset >= 1)
      l.append(v.mid(previousOffset, offset - previousOffset));
    else
      l.append(ByteVector());

    previousOffset = offset + pattern.size();
  }

  if(previousOffset < v.size())
    l.append(v.mid(previousOffset, v.size() - previousOffset));

  return l;
}
ByteVector UnsynchronizedLyricsFrame::renderFields() const
{
  ByteVector v;

  //=== Alex. There is a problem with the Writing of UNICODE strings. That's why we manually set the encoding
  String::Type encoding = String::UTF8;
  v.append(char(encoding));
  v.append(d->language.size() == 3 ? d->language : "XXX");
  v.append(d->description.data(encoding));
  v.append(textDelimiter(encoding));
  v.append(d->text.data(encoding));
  //=== Alex END. There is a problem


  return v;
}
Beispiel #9
0
void Header::parse(const ByteVector &data)
{
  if(data.size() < size())
    return;


  // do some sanity checking -- even in ID3v2.3.0 and less the tag size is a
  // synch-safe integer, so all bytes must be less than 128.  If this is not
  // true then this is an invalid tag.

  // note that we're doing things a little out of order here -- the size is
  // later in the bytestream than the version

  ByteVector sizeData = data.mid(6, 4);

  if(sizeData.size() != 4) {
    d->tagSize = 0;
    debug("TagLib::ID3v2::Header::parse() - The tag size as read was 0 bytes!");
    return;
  }

  for(ByteVector::Iterator it = sizeData.begin(); it != sizeData.end(); it++) {
    if(uchar(*it) >= 128) {
      d->tagSize = 0;
      debug("TagLib::ID3v2::Header::parse() - One of the size bytes in the id3v2 header was greater than the allowed 128.");
      return;
    }
  }

  // The first three bytes, data[0..2], are the File Identifier, "ID3". (structure 3.1 "file identifier")

  // Read the version number from the fourth and fifth bytes.
  d->majorVersion = data[3];   // (structure 3.1 "major version")
  d->revisionNumber = data[4]; // (structure 3.1 "revision number")

  // Read the flags, the first four bits of the sixth byte.
  std::bitset<8> flags(data[5]);

  d->unsynchronisation     = flags[7]; // (structure 3.1.a)
  d->extendedHeader        = flags[6]; // (structure 3.1.b)
  d->experimentalIndicator = flags[5]; // (structure 3.1.c)
  d->footerPresent         = flags[4]; // (structure 3.1.d)

  // Get the size from the remaining four bytes (read above)

  d->tagSize = SynchData::toUInt(sizeData); // (structure 3.1 "size")
}
Beispiel #10
0
  void testUpdateStco()
  {
    string filename = copyFile("no-tags", ".3g2");

    MP4::File *f = new MP4::File(filename.c_str());
    f->tag()->setArtist(ByteVector(3000, 'x'));

    ByteVectorList data1;
    {
      MP4::Atoms a(f);
      MP4::Atom *stco = a.find("moov")->findall("stco", true)[0];
      f->seek(stco->offset + 12);
      ByteVector data = f->readBlock(stco->length - 12);
      unsigned int count = data.mid(0, 4).toUInt();
      int pos = 4;
      while (count--) {
        unsigned int offset = data.mid(pos, 4).toUInt();
        f->seek(offset);
        data1.append(f->readBlock(20));
        pos += 4;
      }
    }

    f->save();
    delete f;
    f = new MP4::File(filename.c_str());

    {
      MP4::Atoms a(f);
      MP4::Atom *stco = a.find("moov")->findall("stco", true)[0];
      f->seek(stco->offset + 12);
      ByteVector data = f->readBlock(stco->length - 12);
      unsigned int count = data.mid(0, 4).toUInt();
      int pos = 4, i = 0;
      while (count--) {
        unsigned int offset = data.mid(pos, 4).toUInt();
        f->seek(offset);
        CPPUNIT_ASSERT_EQUAL(data1[i], f->readBlock(20));
        pos += 4;
        i++;
      }
    }

    delete f;

    deleteFile(filename);
  }
Beispiel #11
0
ByteVector CommentsFrame::renderFields() const
{
  ByteVector v;

  String::Type encoding = d->textEncoding;

  encoding = checkEncoding(d->description, encoding);
  encoding = checkEncoding(d->text, encoding);

  v.append(char(encoding));
  v.append(d->language.size() == 3 ? d->language : "XXX");
  v.append(d->description.data(encoding));
  v.append(textDelimiter(encoding));
  v.append(d->text.data(encoding));

  return v;
}
Beispiel #12
0
ByteVector OwnershipFrame::renderFields() const
{
  StringList sl;
  sl.append(d->seller);

  const String::Type encoding = checkTextEncoding(sl, d->textEncoding);

  ByteVector v;

  v.append(char(encoding));
  v.append(d->pricePaid.data(String::Latin1));
  v.append(textDelimiter(String::Latin1));
  v.append(d->datePurchased.data(String::Latin1));
  v.append(d->seller.data(encoding));

  return v;
}
Beispiel #13
0
MP4::AtomDataList
MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm)
{
  AtomDataList result;
  ByteVector data = d->file->readBlock(atom->length - 8);
  int i = 0;
  unsigned int pos = 0;
  while(pos < data.size()) {
    const int length = static_cast<int>(data.toUInt(pos));
    if(length < 12) {
      debug("MP4: Too short atom");
      return result;
    }

    const ByteVector name = data.mid(pos + 4, 4);
    const int flags = static_cast<int>(data.toUInt(pos + 8));
    if(freeForm && i < 2) {
      if(i == 0 && name != "mean") {
        debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
        return result;
      }
      else if(i == 1 && name != "name") {
        debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
        return result;
      }
      result.append(AtomData(AtomDataType(flags), data.mid(pos + 12, length - 12)));
    }
    else {
      if(name != "data") {
        debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
        return result;
      }
      if(expectedFlags == -1 || flags == expectedFlags) {
        result.append(AtomData(AtomDataType(flags), data.mid(pos + 16, length - 16)));
      }
    }
    pos += length;
    i++;
  }
  return result;
}
Beispiel #14
0
ByteVector RelativeVolumeFrame::renderFields() const
{
  ByteVector data;

  data.append(d->identification.data(String::Latin1));
  data.append(textDelimiter(String::Latin1));

  Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin();

  for(; it != d->channels.end(); ++it) {
    ChannelType type = (*it).first;
    const ChannelData &channel = (*it).second;

    data.append(char(type));
    data.append(ByteVector::fromShort(channel.volumeAdjustment));
    data.append(char(channel.peakVolume.bitsRepresentingPeak));
    data.append(channel.peakVolume.peakVolume);
  }

  return data;
}
Beispiel #15
0
void TextIdentificationFrame::parseFields(const ByteVector &data)
{
  // read the string data type (the first byte of the field data)

  d->textEncoding = String::Type(data[0]);

  // split the byte array into chunks based on the string type (two byte delimiter
  // for unicode encodings)

  int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;

  ByteVectorList l = ByteVectorList::split(data.mid(1), textDelimiter(d->textEncoding), byteAlign);

  d->fieldList.clear();

  // append those split values to the list and make sure that the new string's
  // type is the same specified for this frame

  for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
    String s(*it, d->textEncoding);
    d->fieldList.append(s);
  }
}
ByteVector UnsynchronizedLyricsFrame::renderFields() const
{
  StringList sl;
  sl.append(d->description);
  sl.append(d->text);

  const String::Type encoding = checkTextEncoding(sl, d->textEncoding);

  ByteVector v;

  v.append(char(encoding));
  v.append(d->language.size() == 3 ? d->language : "XXX");
  v.append(d->description.data(encoding));
  v.append(textDelimiter(encoding));
  v.append(d->text.data(encoding));

  return v;
}
Beispiel #17
0
// --------- typed value to specific type conversions
bool Registry::ValueToStringList(const ByteVector& value, StringList& list)
{
    ByteVector::const_iterator str_start= value.begin();
    for (ByteVector::const_iterator i= value.begin() ; i!=value.end() ; ++i)
    {
        if (*i==0)
        {
            list.push_back(string(str_start, i));
            str_start= i+1;
        }
    }
    if (str_start!=value.end())
    {
        list.push_back(string(str_start, value.end()));
        str_start= value.end();
    }
    return true;
}
/*----------------------------------------------------------------------------*/
void DDAProtocol :: pushData(unsigned char header, const DDAProtocol :: ByteVector &data)
{
  ByteVector txData;
  unsigned size = data.size();
  unsigned char crc = 0;
  txData.push_back(ENQ);
  txData.push_back(header);
  crc += header;
  txData.push_back(size + 3);
  crc += size + 3;
  for(unsigned i = 0; i < size; i++)
  {
    txData.push_back(data[i]);
    crc += data[i];
  }
  txData.push_back(crc);
  m_txData.push_back(txData);
}
void TableOfContentsFrame::parseFields(const ByteVector &data)
{
  uint size = data.size();
  if(size < 6) {
    debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated by null.");
    return;
  }

  int pos = 0, embPos = 0;
  d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
  d->elementID.append(char(0));
  d->isTopLevel = (data.at(pos) & 2) > 0;
  d->isOrdered = (data.at(pos++) & 1) > 0;
  uint entryCount = data.at(pos++);
  for(uint i = 0; i < entryCount; i++)
  {
    ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
    childElementID.append(char(0));
    d->childElements.append(childElementID);
  }

  size -= pos;
  while((uint)embPos < size - Frame::headerSize(4))
  {
    Frame *frame = d->factory->createFrame(data.mid(pos + embPos));

    if(!frame)
      return;

    // Checks to make sure that frame parsed correctly.
    if(frame->size() <= 0) {
      delete frame;
      return;
    }

    embPos += frame->size() + Frame::headerSize(4);
    addEmbeddedFrame(frame);
  }
}
Beispiel #20
0
void ChapterFrame::parseFields(const ByteVector &data)
{
  unsigned int size = data.size();
  if(size < 18) {
    debug("A CHAP frame must contain at least 18 bytes (1 byte element ID "
          "terminated by null and 4x4 bytes for start and end time and offset).");
    return;
  }

  int pos = 0;
  unsigned int embPos = 0;
  d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
  d->startTime = data.toUInt(pos, true);
  pos += 4;
  d->endTime = data.toUInt(pos, true);
  pos += 4;
  d->startOffset = data.toUInt(pos, true);
  pos += 4;
  d->endOffset = data.toUInt(pos, true);
  pos += 4;
  size -= pos;

  // Embedded frames are optional

  if(size < header()->size())
    return;

  while(embPos < size - header()->size()) {
    Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0));

    if(!frame)
      return;

    // Checks to make sure that frame parsed correctly.
    if(frame->size() <= 0) {
      delete frame;
      return;
    }

    embPos += frame->size() + header()->size();
    addEmbeddedFrame(frame);
  }
}
void ByteVectorStream::insert(const ByteVector &data, long long start, size_t replace)
{
  if(data.size() < replace) {
    removeBlock(start + data.size(), replace - data.size());
  }
  else if(data.size() > replace) {
    const size_t sizeDiff = data.size() - replace;
    truncate(length() + sizeDiff);

    const size_t readPosition  = static_cast<size_t>(start + replace);
    const size_t writePosition = static_cast<size_t>(start + data.size());
    ::memmove(
      d->data.data() + writePosition,
      d->data.data() + readPosition,
      static_cast<size_t>(length() - sizeDiff - readPosition));
  }
  seek(start);
  writeBlock(data);
}
Beispiel #22
0
ByteVector Footer::render(bool isHeader) const
{
  ByteVector v;

  // add the file identifier -- "APETAGEX"

  v.append(fileIdentifier());

  // add the version number -- we always render a 2.000 tag regardless of what
  // the tag originally was.

  v.append(ByteVector::fromUInt(2000, false));

  // add the tag size

  v.append(ByteVector::fromUInt(d->tagSize, false));

  // add the item count

  v.append(ByteVector::fromUInt(d->itemCount, false));

  // render and add the flags

  std::bitset<32> flags;

  flags[31] = d->headerPresent;
  flags[30] = false; // footer is always present
  flags[29] = isHeader;

  v.append(ByteVector::fromUInt(flags.to_ulong(), false));

  // add the reserved 64bit

  v.append(ByteVector::fromLongLong(0));

  return v;
}
Beispiel #23
0
ByteVector ID3v2::Tag::render() const
{
  // We need to render the "tag data" first so that we have to correct size to
  // render in the tag's header.  The "tag data" -- everything that is included
  // in ID3v2::Header::tagSize() -- includes the extended header, frames and
  // padding, but does not include the tag's header or footer.

  ByteVector tagData;

  // TODO: Render the extended header.

  // Loop through the frames rendering them and adding them to the tagData.

  for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) {
    if ((*it)->header()->frameID().size() != 4) {
      debug("A frame of unsupported or unknown type \'"
          + String((*it)->header()->frameID()) + "\' has been discarded");
      continue;
    }
    if(!(*it)->header()->tagAlterPreservation())
      tagData.append((*it)->render());
  }

  // Compute the amount of padding, and append that to tagData.

  uint paddingSize = 0;
  uint originalSize = d->header.tagSize();

  if(tagData.size() < originalSize)
    paddingSize = originalSize - tagData.size();
  else
    paddingSize = 1024;

  tagData.append(ByteVector(paddingSize, char(0)));

  // Set the tag size.
  d->header.setTagSize(tagData.size());

  // TODO: This should eventually include d->footer->render().
  return d->header.render() + tagData;
}
Beispiel #24
0
ByteVectorList
MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
{
  ByteVectorList result;
  ByteVector data = file->readBlock(atom->length - 8);
  int i = 0;
  unsigned int pos = 0;
  while(pos < data.size()) {
    int length = data.mid(pos, 4).toUInt();
    ByteVector name = data.mid(pos + 4, 4);
    int flags = data.mid(pos + 8, 4).toUInt();
    if(freeForm && i < 2) {
      if(i == 0 && name != "mean") {
        debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
        return result;
      }
      else if(i == 1 && name != "name") {
        debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
        return result;
      }
      result.append(data.mid(pos + 12, length - 12));
    }
    else {
      if(name != "data") {
        debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
        return result;
      }
      if(expectedFlags == -1 || flags == expectedFlags) {
        result.append(data.mid(pos + 16, length - 16));
      }
    }
    pos += length;
    i++;
  }
  return result;
}
ByteVector TableOfContentsFrame::renderFields() const
{
  ByteVector data;

  data.append(d->elementID);
  char flags = 0;
  if(d->isTopLevel)
    flags += 2;
  if(d->isOrdered)
    flags += 1;
  data.append(flags);
  data.append((char)(entryCount()));
  ByteVectorList::ConstIterator it = d->childElements.begin();
  while(it != d->childElements.end()) {
    data.append(*it);
    it++;
  }
  FrameList l = d->embeddedFrameList;
  for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
    data.append((*it)->render());

  return data;
}
Beispiel #26
0
void
MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
{
  MP4::CoverArtList value;
  ByteVector data = file->readBlock(atom->length - 8);
  unsigned int pos = 0;
  while(pos < data.size()) {
    int length = data.mid(pos, 4).toUInt();
    ByteVector name = data.mid(pos + 4, 4);
    int flags = data.mid(pos + 8, 4).toUInt();
    if(name != "data") {
      debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
      return;
    }
    if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) {
      value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
                                 data.mid(pos + 16, length - 16)));
    }
    pos += length;
  }
  if(value.size() > 0)
    d->items.insert(atom->name, value);
}
Beispiel #27
0
void APE::Properties::analyzeOld()
{
  ByteVector header = d->file->readBlock(26);
  uint totalFrames = header.mid(18, 4).toUInt(false);

  // Fail on 0 length APE files (catches non-finalized APE files)
  if(totalFrames == 0)
    return;

  short compressionLevel = header.mid(0, 2).toShort(false);
  uint blocksPerFrame;
  if(d->version >= 3950)
    blocksPerFrame = 73728 * 4;
  else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
    blocksPerFrame = 73728;
  else
    blocksPerFrame = 9216;
  d->channels = header.mid(4, 2).toShort(false);
  d->sampleRate = header.mid(6, 4).toUInt(false);
  uint finalFrameBlocks = header.mid(22, 4).toUInt(false);
  uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
  d->length = totalBlocks / d->sampleRate;
  d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}
Beispiel #28
0
String Frame::readStringField(const ByteVector &data, String::Type encoding, int *position)
{
    int start = 0;

    if(!position)
        position = &start;

    ByteVector delimiter = textDelimiter(encoding);

    int end = data.find(delimiter, *position, delimiter.size());

    if(end < *position)
        return String::null;

    String str;
    if(encoding == String::Latin1)
        str = Tag::latin1StringHandler()->parse(data.mid(*position, end - *position));
    else
        str = String(data.mid(*position, end - *position), encoding);

    *position = end + delimiter.size();

    return str;
}
Beispiel #29
0
void Frame::Header::setFrameID(const ByteVector &id)
{
  d->frameID = id.mid(0, 4);
}
Beispiel #30
0
void Frame::Header::setData(const ByteVector &data, uint version)
{
  d->version = version;

  switch(version) {
  case 0:
  case 1:
  case 2:
  {
    // ID3v2.2

    if(data.size() < 3) {
      debug("You must at least specify a frame ID.");
      return;
    }

    // Set the frame ID -- the first three bytes

    d->frameID = data.mid(0, 3);

    // If the full header information was not passed in, do not continue to the
    // steps to parse the frame size and flags.

    if(data.size() < 6) {
      d->frameSize = 0;
      return;
    }

    d->frameSize = data.mid(3, 3).toUInt();

    break;
  }
  case 3:
  {
    // ID3v2.3

    if(data.size() < 4) {
      debug("You must at least specify a frame ID.");
      return;
    }

    // Set the frame ID -- the first four bytes

    d->frameID = data.mid(0, 4);

    // If the full header information was not passed in, do not continue to the
    // steps to parse the frame size and flags.

    if(data.size() < 10) {
      d->frameSize = 0;
      return;
    }

    // Set the size -- the frame size is the four bytes starting at byte four in
    // the frame header (structure 4)

    d->frameSize = data.mid(4, 4).toUInt();

    { // read the first byte of flags
      std::bitset<8> flags(data[8]);
      d->tagAlterPreservation  = flags[7]; // (structure 3.3.1.a)
      d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b)
      d->readOnly              = flags[5]; // (structure 3.3.1.c)
    }

    { // read the second byte of flags
      std::bitset<8> flags(data[9]);
      d->compression         = flags[7]; // (structure 3.3.1.i)
      d->encryption          = flags[6]; // (structure 3.3.1.j)
      d->groupingIdentity    = flags[5]; // (structure 3.3.1.k)
    }
    break;
  }
  case 4:
  default:
  {
    // ID3v2.4

    if(data.size() < 4) {
      debug("You must at least specify a frame ID.");
      return;
    }

    // Set the frame ID -- the first four bytes

    d->frameID = data.mid(0, 4);

    // If the full header information was not passed in, do not continue to the
    // steps to parse the frame size and flags.

    if(data.size() < 10) {
      d->frameSize = 0;
      return;
    }

    // Set the size -- the frame size is the four bytes starting at byte four in
    // the frame header (structure 4)

    d->frameSize = SynchData::toUInt(data.mid(4, 4));
#ifndef NO_ITUNES_HACKS
    // iTunes writes v2.4 tags with v2.3-like frame sizes
    if(d->frameSize > 127) {
      if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
        unsigned int uintSize = data.mid(4, 4).toUInt();
        if(isValidFrameID(data.mid(uintSize + 10, 4))) {
          d->frameSize = uintSize;
        }
      }
    }
#endif

    { // read the first byte of flags
      std::bitset<8> flags(data[8]);
      d->tagAlterPreservation  = flags[6]; // (structure 4.1.1.a)
      d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b)
      d->readOnly              = flags[4]; // (structure 4.1.1.c)
    }

    { // read the second byte of flags
      std::bitset<8> flags(data[9]);
      d->groupingIdentity    = flags[6]; // (structure 4.1.2.h)
      d->compression         = flags[3]; // (structure 4.1.2.k)
      d->encryption          = flags[2]; // (structure 4.1.2.m)
      d->unsynchronisation   = flags[1]; // (structure 4.1.2.n)
      d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p)
    }
    break;
  }
  }
}