Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
0
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;
}
Exemplo n.º 8
0
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;
}
Exemplo n.º 9
0
//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;
}
Exemplo n.º 10
0
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
}
Exemplo n.º 11
0
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;
} 
Exemplo n.º 12
0
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;
}
Exemplo n.º 13
0
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;
}
Exemplo n.º 14
0
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;
}