void AttachedPictureFrame::parseFields(const ByteVector &data)
{
    if(data.size() < 5) {
        debug("A picture frame must contain at least 5 bytes.");
        return;
    }

    int pos = 0;

    d->textEncoding = String::Type(data[pos]);
    pos += 1;

    int offset = data.find(textDelimiter(String::Latin1), pos);
    if(offset < pos)
        return;
    d->mimeType = String(data.mid(pos, offset - pos), String::Latin1);
    pos = offset + 1;

    d->type = Type(data[pos]);
    pos += 1;

    offset = data.find(textDelimiter(d->textEncoding), pos);
    if(offset < pos)
        return;
    d->description = String(data.mid(pos, offset - pos), d->textEncoding);
    pos = offset + 1;

    d->data = data.mid(pos);
}
Exemple #2
0
void ASF::Picture::parse(const ByteVector& bytes)
{
  d->valid = false;
  if(bytes.size() < 9)
    return;
  int pos = 0;
  d->type = (Type)bytes[0]; ++pos;
  uint dataLen = bytes.mid(pos, 4).toUInt(false); pos+=4;

  const ByteVector nullStringTerminator(2, 0);

  int endPos = bytes.find(nullStringTerminator, pos, 2);
  if(endPos < 0)
    return;
  d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
  pos = endPos+2;

  endPos = bytes.find(nullStringTerminator, pos, 2);
  if(endPos < 0)
    return;
  d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
  pos = endPos+2;

  if(dataLen + pos != bytes.size())
    return;

  d->picture = bytes.mid(pos, dataLen);
  d->valid = true;
  return;
}
Exemple #3
0
void UserUrlLinkFrame::parseFields(const ByteVector &data)
{
  if(data.size() < 2) {
    debug("A user URL link frame must contain at least 2 bytes.");
    return;
  }

  int pos = 0;

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

  if(d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) {
    int offset = data.find(textDelimiter(d->textEncoding), pos);
    if(offset < pos)
      return;

    d->description = String(data.mid(pos, offset - pos), d->textEncoding);
    pos = offset + 1;
  }
  else {
    int len = data.mid(pos).find(textDelimiter(d->textEncoding), 0, 2);
    if(len < 0)
      return;

    d->description = String(data.mid(pos, len), d->textEncoding);
    pos += len + 2;
  }

  setUrl(String(data.mid(pos)));
}
Exemple #4
0
void APE::Item::parse(const ByteVector &data)
{
  // 11 bytes is the minimum size for an APE item

  if(data.size() < 11) {
    debug("APE::Item::parse() -- no data in item");
    return;
  }

  const uint valueLength  = data.toUInt(0, false);
  const uint flags        = data.toUInt(4, false);
  
  const int keyLength = data.find('\0', 8) - 8;
  if(keyLength < 1 || keyLength > 255)
  {
    debug("APE::Item::parse() -- invalid key in item");
    return;
  }

  d->key = String(data.mid(8, keyLength), String::UTF8);
  const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);

  setReadOnly(flags & 1);
  setType(ItemTypes((flags >> 1) & 3));

  if(Text == d->type) 
    d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
  else
    d->value = value;
}
void RelativeVolumeFrame::parseFields(const ByteVector &data)
{
  uint pos = data.find(textDelimiter(String::Latin1));
  d->identification = String(data.mid(0, pos), String::Latin1);

  // Each channel is at least 4 bytes.

  while(pos <= data.size() - 4) {


    ChannelType type = ChannelType(data[pos]);
    pos += 1;

    ChannelData &channel = d->channels[type];

    channel.volumeAdjustment = data.mid(pos, 2).toShort();
    pos += 2;

    channel.peakVolume.bitsRepresentingPeak = data[pos];
    pos += 1;

    int bytes = bitsToBytes(channel.peakVolume.bitsRepresentingPeak);
    channel.peakVolume.peakVolume = data.mid(pos, bytes);
    pos += bytes;
  }
}
  void testResize()
  {
    ByteVector a = ByteVector("0123456789");
    ByteVector b = a.mid(3, 4);
    b.resize(6, 'A');
    CPPUNIT_ASSERT_EQUAL(uint(6), b.size());
    CPPUNIT_ASSERT_EQUAL('6', b[3]);
    CPPUNIT_ASSERT_EQUAL('A', b[4]);
    CPPUNIT_ASSERT_EQUAL('A', b[5]);
    b.resize(10, 'B');
    CPPUNIT_ASSERT_EQUAL(uint(10), b.size());
    CPPUNIT_ASSERT_EQUAL('6', b[3]);
    CPPUNIT_ASSERT_EQUAL('B', b[6]);
    CPPUNIT_ASSERT_EQUAL('B', b[9]);
    b.resize(3, 'C');
    CPPUNIT_ASSERT_EQUAL(uint(3), b.size());
    CPPUNIT_ASSERT_EQUAL(-1, b.find('C'));
    b.resize(3);
    CPPUNIT_ASSERT_EQUAL(uint(3), b.size());

    // Check if a and b were properly detached.

    CPPUNIT_ASSERT_EQUAL(uint(10), a.size());
    CPPUNIT_ASSERT_EQUAL('3', a[3]);
    CPPUNIT_ASSERT_EQUAL('5', a[5]);

    // Special case that refCount == 1 and d->offset != 0.

    ByteVector c = ByteVector("0123456789").mid(3, 4);
    c.resize(6, 'A');
    CPPUNIT_ASSERT_EQUAL(uint(6), c.size());
    CPPUNIT_ASSERT_EQUAL('6', c[3]);
    CPPUNIT_ASSERT_EQUAL('A', c[4]);
    CPPUNIT_ASSERT_EQUAL('A', c[5]);
    c.resize(10, 'B');
    CPPUNIT_ASSERT_EQUAL(uint(10), c.size());
    CPPUNIT_ASSERT_EQUAL('6', c[3]);
    CPPUNIT_ASSERT_EQUAL('B', c[6]);
    CPPUNIT_ASSERT_EQUAL('B', c[9]);
    c.resize(3, 'C');
    CPPUNIT_ASSERT_EQUAL(uint(3), c.size());
    CPPUNIT_ASSERT_EQUAL(-1, c.find('C'));
  }
Exemple #7
0
 uint read(TagLib::File &file, uint limit)
 {
   ByteVector data = file.readBlock(std::min(m_size, limit));
   uint count = data.size();
   int index = data.find((char) 0);
   if(index > -1) {
     data.resize(index);
   }
   data.replace((char) 0xff, ' ');
   value = data;
   return count;
 }
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;
}
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;
}
Exemple #10
0
void PrivateFrame::parseFields(const ByteVector &data)
{
    if(data.size() < 2) {
        debug("A private frame must contain at least 2 bytes.");
        return;
    }

    // Owner identifier is assumed to be Latin1

    const int byteAlign =  1;
    const int endOfOwner = data.find(textDelimiter(String::Latin1), 0, byteAlign);

    d->owner =  String(data.mid(0, endOfOwner));
    d->data = data.mid(endOfOwner + 1);
}
Exemple #11
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 = String(data.mid(*position, end - *position), encoding);

  *position = end + delimiter.size();

  return str;
}
Exemple #12
0
long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
    if(!d->stream || pattern.size() > d->bufferSize)
        return -1;

    // The position in the file that the current buffer starts at.

    ByteVector buffer;

    // These variables are used to keep track of a partial match that happens at
    // the end of a buffer.

    /*
    int previousPartialMatch = -1;
    int beforePreviousPartialMatch = -1;
    */

    // Save the location of the current read pointer.  We will restore the
    // position using seek() before all returns.

    long originalPosition = tell();

    // Start the search at the offset.

    long bufferOffset;
    if(fromOffset == 0) {
        seek(-1 * int(d->bufferSize), End);
        bufferOffset = tell();
    }
    else {
        seek(fromOffset + -1 * int(d->bufferSize), Beginning);
        bufferOffset = tell();
    }

    // See the notes in find() for an explanation of this algorithm.

    for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {

        // TODO: (1) previous partial match

        // (2) pattern contained in current buffer

        long location = buffer.rfind(pattern);
        if(location >= 0) {
            seek(originalPosition);
            return bufferOffset + location;
        }

        if(!before.isNull() && buffer.find(before) >= 0) {
            seek(originalPosition);
            return -1;
        }

        // TODO: (3) partial match

        bufferOffset -= d->bufferSize;
        seek(bufferOffset);
    }

    // Since we hit the end of the file, reset the status before continuing.

    clear();

    seek(originalPosition);

    return -1;
}
Exemple #13
0
long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
    if(!d->stream || pattern.size() > d->bufferSize)
        return -1;

    // The position in the file that the current buffer starts at.

    long bufferOffset = fromOffset;
    ByteVector buffer;

    // These variables are used to keep track of a partial match that happens at
    // the end of a buffer.

    int previousPartialMatch = -1;
    int beforePreviousPartialMatch = -1;

    // Save the location of the current read pointer.  We will restore the
    // position using seek() before all returns.

    long originalPosition = tell();

    // Start the search at the offset.

    seek(fromOffset);

    // This loop is the crux of the find method.  There are three cases that we
    // want to account for:
    //
    // (1) The previously searched buffer contained a partial match of the search
    // pattern and we want to see if the next one starts with the remainder of
    // that pattern.
    //
    // (2) The search pattern is wholly contained within the current buffer.
    //
    // (3) The current buffer ends with a partial match of the pattern.  We will
    // note this for use in the next itteration, where we will check for the rest
    // of the pattern.
    //
    // All three of these are done in two steps.  First we check for the pattern
    // and do things appropriately if a match (or partial match) is found.  We
    // then check for "before".  The order is important because it gives priority
    // to "real" matches.

    for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {

        // (1) previous partial match

        if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) {
            const int patternOffset = (d->bufferSize - previousPartialMatch);
            if(buffer.containsAt(pattern, 0, patternOffset)) {
                seek(originalPosition);
                return bufferOffset - d->bufferSize + previousPartialMatch;
            }
        }

        if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) {
            const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch);
            if(buffer.containsAt(before, 0, beforeOffset)) {
                seek(originalPosition);
                return -1;
            }
        }

        // (2) pattern contained in current buffer

        long location = buffer.find(pattern);
        if(location >= 0) {
            seek(originalPosition);
            return bufferOffset + location;
        }

        if(!before.isNull() && buffer.find(before) >= 0) {
            seek(originalPosition);
            return -1;
        }

        // (3) partial match

        previousPartialMatch = buffer.endsWithPartialMatch(pattern);

        if(!before.isNull())
            beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);

        bufferOffset += d->bufferSize;
    }

    // Since we hit the end of the file, reset the status before continuing.

    clear();

    seek(originalPosition);

    return -1;
}
Exemple #14
0
void IT::File::read(bool)
{
  if(!isOpen())
    return;

  seek(0);
  READ_ASSERT(readBlock(4) == "IMPM");
  READ_STRING(d->tag.setTitle, 26);

  seek(2, Current);

  READ_U16L_AS(length);
  READ_U16L_AS(instrumentCount);
  READ_U16L_AS(sampleCount);

  d->properties.setInstrumentCount(instrumentCount);
  d->properties.setSampleCount(sampleCount);
  READ_U16L(d->properties.setPatternCount);
  READ_U16L(d->properties.setVersion);
  READ_U16L(d->properties.setCompatibleVersion);
  READ_U16L(d->properties.setFlags);
  READ_U16L_AS(special);
  d->properties.setSpecial(special);
  READ_BYTE(d->properties.setGlobalVolume);
  READ_BYTE(d->properties.setMixVolume);
  READ_BYTE(d->properties.setBpmSpeed);
  READ_BYTE(d->properties.setTempo);
  READ_BYTE(d->properties.setPanningSeparation);
  READ_BYTE(d->properties.setPitchWheelDepth);

  // IT supports some kind of comment tag. Still, the
  // sample/instrument names are abused as comments so
  // I just add all together.
  String message;
  if(special & Properties::MessageAttached) {
    READ_U16L_AS(messageLength);
    READ_U32L_AS(messageOffset);
    seek(messageOffset);
    ByteVector messageBytes = readBlock(messageLength);
    READ_ASSERT(messageBytes.size() == messageLength);
    int index = messageBytes.find((char) 0);
    if(index > -1)
      messageBytes.resize(index, 0);
    messageBytes.replace('\r', '\n');
    message = messageBytes;
  }

  seek(64);

  ByteVector pannings = readBlock(64);
  ByteVector volumes  = readBlock(64);
  READ_ASSERT(pannings.size() == 64 && volumes.size() == 64);
  int channels = 0;
  for(int i = 0; i < 64; ++ i) {
    // Strictly speaking an IT file has always 64 channels, but
    // I don't count disabled and muted channels.
    // But this always gives 64 channels for all my files anyway.
    // Strangely VLC does report other values. I wonder how VLC
    // gets it's values.
    if((unsigned char) pannings[i] < 128 && volumes[i] > 0)
        ++channels;
  }
  d->properties.setChannels(channels);

  // real length might be shorter because of skips and terminator
  ushort realLength = 0;
  for(ushort i = 0; i < length; ++ i) {
    READ_BYTE_AS(order);
    if(order == 255) break;
    if(order != 254) ++ realLength;
  }
  d->properties.setLengthInPatterns(realLength);

  StringList comment;
  // Note: I found files that have nil characters somewhere
  //       in the instrument/sample names and more characters
  //       afterwards. The spec does not mention such a case.
  //       Currently I just discard anything after a nil, but
  //       e.g. VLC seems to interprete a nil as a space. I
  //       don't know what is the proper behaviour.
  for(ushort i = 0; i < instrumentCount; ++ i) {
    seek(192L + length + ((long)i << 2));
    READ_U32L_AS(instrumentOffset);
    seek(instrumentOffset);

    ByteVector instrumentMagic = readBlock(4);
    READ_ASSERT(instrumentMagic == "IMPI");

    READ_STRING_AS(dosFileName, 13);

    seek(15, Current);

    READ_STRING_AS(instrumentName, 26);
    comment.append(instrumentName);
  }

  for(ushort i = 0; i < sampleCount; ++ i) {
    seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
    READ_U32L_AS(sampleOffset);

    seek(sampleOffset);

    ByteVector sampleMagic = readBlock(4);
    READ_ASSERT(sampleMagic == "IMPS");

    READ_STRING_AS(dosFileName, 13);
    READ_BYTE_AS(globalVolume);
    READ_BYTE_AS(sampleFlags);
    READ_BYTE_AS(sampleVolume);
    READ_STRING_AS(sampleName, 26);
    /*
    READ_BYTE_AS(sampleCvt);
    READ_BYTE_AS(samplePanning);
    READ_U32L_AS(sampleLength);
    READ_U32L_AS(loopStart);
    READ_U32L_AS(loopStop);
    READ_U32L_AS(c5speed);
    READ_U32L_AS(sustainLoopStart);
    READ_U32L_AS(sustainLoopEnd);
    READ_U32L_AS(sampleDataOffset);
    READ_BYTE_AS(vibratoSpeed);
    READ_BYTE_AS(vibratoDepth);
    READ_BYTE_AS(vibratoRate);
    READ_BYTE_AS(vibratoType);
    */

    comment.append(sampleName);
  }

  if(message.size() > 0)
    comment.append(message);
  d->tag.setComment(comment.toString("\n"));
  d->tag.setTrackerName("Impulse Tracker");
}
long MPEG::File::findID3v2()
{
  // This method is based on the contents of TagLib::File::find(), but because
  // of some subtlteies -- specifically the need to look for the bit pattern of
  // an MPEG sync, it has been modified for use here.

  if(isValid() && ID3v2::Header::fileIdentifier().size() <= bufferSize()) {

    // The position in the file that the current buffer starts at.

    long bufferOffset = 0;
    ByteVector buffer;

    // These variables are used to keep track of a partial match that happens at
    // the end of a buffer.

    int previousPartialMatch = -1;
    bool previousPartialSynchMatch = false;

    // Save the location of the current read pointer.  We will restore the
    // position using seek() before all returns.

    long originalPosition = tell();

    // Start the search at the beginning of the file.

    seek(0);

    // This loop is the crux of the find method.  There are three cases that we
    // want to account for:
    // (1) The previously searched buffer contained a partial match of the search
    // pattern and we want to see if the next one starts with the remainder of
    // that pattern.
    //
    // (2) The search pattern is wholly contained within the current buffer.
    //
    // (3) The current buffer ends with a partial match of the pattern.  We will
    // note this for use in the next itteration, where we will check for the rest
    // of the pattern.

    for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {

      // (1) previous partial match

      if(previousPartialSynchMatch && secondSynchByte(buffer[0]))
        return -1;

      if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) {
        const int patternOffset = (bufferSize() - previousPartialMatch);
        if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) {
          seek(originalPosition);
          return bufferOffset - bufferSize() + previousPartialMatch;
        }
      }

      // (2) pattern contained in current buffer

      long location = buffer.find(ID3v2::Header::fileIdentifier());
      if(location >= 0) {
        seek(originalPosition);
        return bufferOffset + location;
      }

      int firstSynchByte = buffer.find(char(uchar(255)));

      // Here we have to loop because there could be several of the first
      // (11111111) byte, and we want to check all such instances until we find
      // a full match (11111111 111) or hit the end of the buffer.

      while(firstSynchByte >= 0) {

        // if this *is not* at the end of the buffer

        if(firstSynchByte < int(buffer.size()) - 1) {
          if(secondSynchByte(buffer[firstSynchByte + 1])) {
            // We've found the frame synch pattern.
            seek(originalPosition);
            return -1;
          }
          else {

            // We found 11111111 at the end of the current buffer indicating a
            // partial match of the synch pattern.  The find() below should
            // return -1 and break out of the loop.

            previousPartialSynchMatch = true;
          }
        }

        // Check in the rest of the buffer.

        firstSynchByte = buffer.find(char(uchar(255)), firstSynchByte + 1);
      }

      // (3) partial match

      previousPartialMatch = buffer.endsWithPartialMatch(ID3v2::Header::fileIdentifier());

      bufferOffset += bufferSize();
    }

    // Since we hit the end of the file, reset the status before continuing.

    clear();

    seek(originalPosition);
  }

  return -1;
}
Exemple #16
0
long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
  if(!d->stream || pattern.size() > bufferSize())
      return -1;

  // The position in the file that the current buffer starts at.

  ByteVector buffer;

  // These variables are used to keep track of a partial match that happens at
  // the end of a buffer.

  /*
  int previousPartialMatch = -1;
  int beforePreviousPartialMatch = -1;
  */

  // Save the location of the current read pointer.  We will restore the
  // position using seek() before all returns.

  long originalPosition = tell();

  // Start the search at the offset.

  if(fromOffset == 0)
    fromOffset = length();

  long bufferLength = bufferSize();
  long bufferOffset = fromOffset + pattern.size();

  // See the notes in find() for an explanation of this algorithm.

  while(true) {

    if(bufferOffset > bufferLength) {
      bufferOffset -= bufferLength;
    }
    else {
      bufferLength = bufferOffset;
      bufferOffset = 0;
    }
    seek(bufferOffset);

    buffer = readBlock(bufferLength);
    if(buffer.isEmpty())
      break;

    // TODO: (1) previous partial match

    // (2) pattern contained in current buffer

    const long location = buffer.rfind(pattern);
    if(location >= 0) {
      seek(originalPosition);
      return bufferOffset + location;
    }

    if(!before.isEmpty() && buffer.find(before) >= 0) {
      seek(originalPosition);
      return -1;
    }

    // TODO: (3) partial match
  }

  // Since we hit the end of the file, reset the status before continuing.

  clear();

  seek(originalPosition);

  return -1;
}