Example #1
0
File: tag.cpp Project: e1z0/sMule
/** Turns a binary tag into a series of ID3_Frame objects attached to the
 ** tag.
 **
 ** \code
 **   ID3_Tag myTag;
 **   uchar header[ID3_TAGHEADERSIZE];
 **   uchar *buffer;
 **   luint tagSize;
 **
 **   // get ID3_TAGHEADERSIZE from a socket or somewhere
 **   // ...
 **
 **   if ((tagSize = ID3_IsTagHeader(ourSourceBuffer)) > -1)
 **   {
 **     // read a further 'tagSize' bytes in
 **     // from our data source
 **     // ...
 **
 **     if (buffer = new uchar[tagSize])
 **     {
 **       // now we will call ID3_Tag::Parse()
 **       // with these values (explained later)
 **       myTag.Parse(header, buffer);
 **
 **       // do something with the objects,
 **       // like look for titles, artists, etc.
 **       // ...
 **
 **       // free the buffer
 **       delete [] buffer;
 **     }
 **   }
 ** \endcode
 **
 ** \sa ID3_Frame
 ** @param header The byte header read in from the data source.
 ** @param buffer The remainder of the tag (not including the data source)
 **               read in from the data source.
 **/
size_t ID3_Tag::Parse(const uchar header[ID3_TAGHEADERSIZE], const uchar *buffer)
{
  size_t size = ID3_Tag::IsV2Tag(header);
  if (0 == size)
  {
    return 0;
  }
  BString buf;
  buf.reserve(ID3_TagHeader::SIZE + size);
  buf.append(reinterpret_cast<const BString::value_type *>(header),
             ID3_TagHeader::SIZE);
  buf.append(reinterpret_cast<const BString::value_type *>(buffer), size);
  return this->Parse(buf.data(), buf.size());
}
io::CompressedReader::CompressedReader(ID3_Reader& reader, size_type newSize)
  : _uncompressed(new char_type[newSize])
{
  size_type oldSize = reader.remainingBytes();
  
  BString binary = readBinary(reader, oldSize);
  
  int dwResult = ::uncompress(_uncompressed,
               reinterpret_cast<luint*>(&newSize),
               reinterpret_cast<const uchar*>(binary.data()),
               oldSize);
  if (dwResult != Z_OK)
	  newSize = 0;
  this->setBuffer(_uncompressed, newSize);
}
Example #3
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;
}
Example #4
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;
}
Example #5
0
int
main(size_t argc, const char** argv)
{
  ID3D_INIT_DOUT();
  ID3D_INIT_WARNING();
  ID3D_INIT_NOTICE();

  ID3_IStreamReader isr(cin);
  BString orig = io::readAllBinary(isr);
    
  cout << "input size:    " << orig.size() << endl;
  
  cout << endl;

  cout << "=== Testing Synchronization ===" << endl;
  BString synced;

  {
    io::BStringReader sr(orig);
    io::UnsyncedReader ur(sr);

    synced = io::readAllBinary(ur);
  }

  cout << "synced size:   " << synced.size() << endl;

  BString unsynced;

  {
    io::BStringWriter sw(unsynced);
    io::UnsyncedWriter uw(sw);

    uw.writeChars(synced.data(), synced.size());
  }
  
  cout << "unsynced size: " << unsynced.size() << endl;

  BString resynced;

  {
    io::BStringReader sr(unsynced);
    io::UnsyncedReader ur(sr);

    resynced = io::readAllBinary(ur);
  }

  cout << "resynced size: " << resynced.size() << endl;

  if (unsynced == orig)
  {
    cout << "orig == unsynced" << endl;
  }
  else
  {
    cout << "orig != unsynced" << endl;
  }
  if (synced == resynced)
  {
    cout << "synced == resynced" << endl;
  }
  else
  {
    cout << "synced != resynced" << endl;
  }

  cout << endl;
  cout << "=== Testing Trailing Spaces ===" << endl;

  String text;

  {
    io::StringWriter sw(text);
    io::writeTrailingSpaces (sw, "hello, world", 50);
  }

  cout << "new text  = \"" << text << "\"" << endl;

  String origText;

  {
    io::StringReader sr(text);
    origText = io::readTrailingSpaces(sr, 100);
  }

  cout << "orig text = \"" << origText << "\"" << endl;

  cout << endl;
  cout << "=== Testing Binary Numbers ===" << endl;

  String number;

  {
    io::StringWriter sw(number);
    io::writeBENumber(sw, 1234567890, 4);
  }

  cout << "binary number:";
  for (size_t i = 0; i < number.size(); ++i)
  {
    cout << " 0x" << hex << (size_t) (0xFF & number[i]) << dec;
  }
  cout << endl;

  size_t val;

  {
    io::StringReader sr(number);
    val = io::readBENumber(sr, 4);
  }

  cout << "orig number: " << val << endl;

  cout << endl;
  cout << "=== Testing Compression ===" << endl;

  String compressed;
  size_t origSize = orig.size();
  cout << "origSize = " << origSize << endl;
  

  {
    io::StringWriter sw(compressed);
    io::CompressedWriter cw(sw);
    cw.writeChars(orig.data(), orig.size());
    cw.flush();
    if (origSize != cw.getOrigSize())
    {
      origSize = cw.getOrigSize();
      cout << "cw.getOrigSize() = " << origSize << endl;
    }
  }

  cout << "compressed size = " << compressed.size() << endl;

  BString uncompressed;
  
  if (origSize == 0)
  {
    cout << "no compression" << endl;
  }
  else
  {
    io::StringReader sr(compressed);
    io::CompressedReader cr(sr, origSize);

    uncompressed = io::readAllBinary(cr);
    cout << "uncompressed size = " << uncompressed.size() << endl;
  }

  if (uncompressed.size() == orig.size())
  {
    if (uncompressed == orig)
    {
      cout << "orig == uncompressed" << endl;
    }
    else
    {
      cout << "orig != uncompressed" << endl;
    }
  }

  String ascii;
  ascii.reserve(8192);

  for (size_t i = 0; i < 8192; ++i)
  {
    ascii += 'a' + (i % 26);
  }

  cout << endl;
  cout << "ascii.size() = " << ascii.size() << endl;
  String unicode = dami::convert(ascii, ID3TE_ASCII, ID3TE_UTF16BE);
  cout << "uncicode.size() = " << unicode.size() << endl;
  String ascii_2 = dami::convert(unicode, ID3TE_UTF16BE, ID3TE_ASCII);
  if (ascii != ascii_2)
  {
    cout << "ascii != ascii_2" << endl;
  }
  else
  {
    cout << "ascii == ascii_2" << endl;
  }



  return 0;
}