Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

src/tag_parse_musicmatch.cpp

Go to the documentation of this file.
00001 // $Id: tag_parse_musicmatch.cpp,v 1.17 2001/08/26 23:33:29 dmazzoni Exp $
00002 
00003 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
00004 // Copyright 1999, 2000  Scott Thomas Haug
00005 
00006 // This library is free software; you can redistribute it and/or modify it
00007 // under the terms of the GNU Library General Public License as published by
00008 // the Free Software Foundation; either version 2 of the License, or (at your
00009 // option) any later version.
00010 //
00011 // This library is distributed in the hope that it will be useful, but WITHOUT
00012 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00014 // License for more details.
00015 //
00016 // You should have received a copy of the GNU Library General Public License
00017 // along with this library; if not, write to the Free Software Foundation,
00018 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 
00020 // The id3lib authors encourage improvements and optimisations to be sent to
00021 // the id3lib coordinator.  Please see the README file for details on where to
00022 // send such submissions.  See the AUTHORS file for a list of people who have
00023 // contributed to id3lib.  See the ChangeLog file for a list of changes to
00024 // id3lib.  These files are distributed with id3lib at
00025 // http://download.sourceforge.net/id3lib/
00026 
00027 #if defined HAVE_CONFIG_H
00028 #include <config.h>
00029 #endif
00030 
00031 
00032 #include <ctype.h>
00033 #include <stdlib.h>
00034 #include "tag_impl.h"
00035 #include "utils.h"
00036 #include "helpers.h"
00037 #include "io_decorators.h"
00038 #include "io_helpers.h"
00039 
00040 using namespace dami;
00041 
00042 namespace 
00043 {
00044   uint32 readSeconds(ID3_Reader& reader, size_t len)
00045   {
00046     io::ExitTrigger et(reader);
00047     io::WindowedReader wr(reader, len);
00048     ID3_Reader::pos_type beg = wr.getCur();
00049     uint32 seconds = 0;
00050     uint32 cur = 0;
00051     while (!wr.atEnd())
00052     {
00053       ID3_Reader::char_type ch = wr.readChar();
00054       if (':' == ch)
00055       {
00056         seconds += 60 * cur;
00057         cur = 0;
00058       }
00059       else if (!isdigit(ch))
00060       {
00061         return 0;
00062       }
00063       else
00064       {
00065         cur = cur * 10 + (ch - '0');
00066       }
00067     }
00068     et.release();
00069     return seconds + cur;
00070   }
00071   
00072   ID3_Frame* readTextFrame(ID3_Reader& reader, ID3_FrameID id, const String desc = "")
00073   {
00074     uint32 size = io::readLENumber(reader, 2);
00075     ID3D_NOTICE( "readTextFrame: size = " << size );
00076     if (size == 0)
00077     {
00078       return NULL;
00079     }
00080     
00081     String text;
00082     if (ID3FID_SONGLEN != id)
00083     {
00084       io::LineFeedReader lfr(reader);
00085       text = io::readText(lfr, size);
00086       ID3D_NOTICE( "readTextFrame: text = " << text );
00087     }
00088     else
00089     {
00090       text = toString(readSeconds(reader, size) * 1000);
00091       ID3D_NOTICE( "readTextFrame: songlen = " << text );
00092     }
00093     
00094     ID3_Frame* frame = new ID3_Frame(id);
00095     if (frame)
00096     {
00097       if (frame->Contains(ID3FN_TEXT))
00098       {
00099         frame->GetField(ID3FN_TEXT)->Set(text.c_str());
00100       }
00101       else if (frame->Contains(ID3FN_URL))
00102       {
00103         frame->GetField(ID3FN_URL)->Set(text.c_str());
00104       }
00105       if (frame->Contains(ID3FN_LANGUAGE))
00106       {
00107         frame->GetField(ID3FN_LANGUAGE)->Set("XXX");
00108       }
00109       if (frame->Contains(ID3FN_DESCRIPTION))
00110       {
00111         frame->GetField(ID3FN_DESCRIPTION)->Set(desc.c_str());
00112       }
00113     }
00114     return frame;
00115   }
00116 };
00117   
00118 bool mm::parse(ID3_TagImpl& tag, ID3_Reader& rdr)
00119 {
00120   io::ExitTrigger et(rdr);
00121   ID3_Reader::pos_type end = rdr.getCur();
00122   if (end < rdr.getBeg() + 48)
00123   {
00124     ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse, pos = " << end );
00125     return false;
00126   }
00127   
00128   rdr.setCur(end - 48);
00129   String version;
00130   
00131   {
00132     if (io::readText(rdr, 32) != "Brava Software Inc.             ")
00133     {
00134       ID3D_NOTICE( "mm::parse: bailing, couldn't find footer" );
00135       return false;
00136     }
00137     
00138     version = io::readText(rdr, 4);
00139     if (version.size() != 4 || 
00140         !isdigit(version[0]) || version[1] != '.' ||
00141         !isdigit(version[2]) || 
00142         !isdigit(version[3]))
00143     {
00144       ID3D_WARNING( "mm::parse: bailing, nonstandard version = " << version );
00145       return false;
00146     }
00147   }
00148     
00149   ID3_Reader::pos_type beg = rdr.setCur(end - 48);
00150   et.setExitPos(beg);
00151   if (end < 68)
00152   {
00153     ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse offsets, pos = " << end );
00154     return false;
00155   }
00156   rdr.setCur(end - 68);
00157     
00158   io::WindowedReader dataWindow(rdr);
00159   dataWindow.setEnd(rdr.getCur());
00160 
00161   uint32 offsets[5];
00162     
00163   io::WindowedReader offsetWindow(rdr, 20);
00164   for (size_t i = 0; i < 5; ++i)
00165   {
00166     offsets[i] = io::readLENumber(rdr, sizeof(uint32));
00167   }
00168 
00169   size_t metadataSize = 0;
00170   if (version <= "3.00")
00171   {
00172     // All MusicMatch tags up to and including version 3.0 had metadata 
00173     // sections exactly 7868 bytes in length.
00174     metadataSize = 7868;
00175   }
00176   else
00177   {
00178     // MusicMatch tags after version 3.0 had three possible lengths for their
00179     // metadata sections.  We can determine which it was by searching for
00180     // the version section signature that should precede the metadata section
00181     // by exactly 256 bytes.
00182     size_t possibleSizes[] = { 8132, 8004, 7936 };
00183       
00184     for (size_t i = 0; i < sizeof(possibleSizes)/sizeof(size_t); ++i)
00185     {
00186       dataWindow.setCur(dataWindow.getEnd());
00187         
00188       // Our offset will be exactly 256 bytes prior to our potential metadata
00189       // section
00190       size_t offset = possibleSizes[i] + 256;
00191       if (dataWindow.getCur() < offset)
00192       {
00193         // if our filesize is less than the offset, then it can't possibly
00194         // be the correct offset, so try again.
00195         continue;
00196       }
00197       dataWindow.setCur(dataWindow.getCur() - offset);
00198         
00199       // now read in the signature to see if it's a match
00200       if (io::readText(dataWindow, 8) == "18273645")
00201       {
00202         metadataSize = possibleSizes[i];
00203         break;
00204       }
00205     }
00206   }
00207   if (0 == metadataSize)
00208   {
00209     // if we didn't establish a size for the metadata, then something is
00210     // wrong.  probably should log this.
00211     ID3D_WARNING( "mm::parse: bailing, couldn't find meta data signature, end = " << end );
00212     return false;
00213   }
00214     
00215   // parse the offset pointers to determine the actual sizes of all the 
00216   // sections
00217   size_t sectionSizes[5];
00218   size_t tagSize = metadataSize;
00219     
00220   // we already know the size of the last section
00221   sectionSizes[4] = metadataSize;
00222     
00223   size_t lastOffset = 0;
00224   for (int i = 0; i < 5; i++)
00225   {
00226     size_t thisOffset = offsets[i];
00227     //ASSERT(thisOffset > lastOffset);
00228     if (i > 0)
00229     {
00230       size_t sectionSize = thisOffset - lastOffset;
00231       sectionSizes[i-1] = sectionSize;
00232       tagSize += sectionSize;
00233     }
00234     lastOffset = thisOffset;
00235   }
00236     
00237   // now check to see that our tag size is reasonable
00238   if (dataWindow.getEnd() < tagSize)
00239   {
00240     // Ack!  The tag size doesn't jive with the tag's ending position in
00241     // the file.  Bail!
00242     ID3D_WARNING( "mm::parse: bailing, tag size is too big, tag size = " << tagSize << ", end = " << end );
00243     return false;
00244   }
00245 
00246   dataWindow.setBeg(dataWindow.getEnd() - tagSize);
00247   dataWindow.setCur(dataWindow.getBeg());
00248     
00249   // Now calculate the adjusted offsets
00250   offsets[0] = dataWindow.getBeg();
00251   for (size_t i = 0; i < 4; ++i)
00252   {
00253     offsets[i+1] = offsets[i] + sectionSizes[i];
00254   }
00255     
00256   // now check for a tag header and adjust the tag_beg pointer appropriately
00257   if (dataWindow.getBeg() >= 256)
00258   {
00259     rdr.setCur(dataWindow.getBeg() - 256);
00260     if (io::readText(rdr, 8) == "18273645")
00261     {
00262       et.setExitPos(rdr.getCur() - 8);
00263     }
00264     else
00265     {
00266       et.setExitPos(dataWindow.getBeg());
00267     }
00268     dataWindow.setCur(dataWindow.getBeg());
00269   }
00270     
00271   // Now parse the various sections...
00272     
00273   // Parse the image extension at offset 0
00274   dataWindow.setCur(offsets[0]);
00275   String imgExt = io::readTrailingSpaces(dataWindow, 4);
00276     
00277   // Parse the image binary at offset 1
00278   dataWindow.setCur(offsets[1]);
00279   uint32 imgSize = io::readLENumber(dataWindow, 4);
00280   if (imgSize == 0)
00281   {
00282     // no image binary.  don't do anything.
00283   }
00284   else
00285   {
00286     io::WindowedReader imgWindow(dataWindow, imgSize);
00287     if (imgWindow.getEnd() < imgWindow.getBeg() + imgSize)
00288     {
00289       // Ack!  The image size given extends beyond the next offset!  This is 
00290       // not good...  log?
00291     }
00292     else
00293     {
00294       BString imgData = io::readAllBinary(imgWindow);
00295       ID3_Frame* frame = new ID3_Frame(ID3FID_PICTURE);
00296       if (frame)
00297       {
00298         String mimetype("image/");
00299         mimetype += imgExt;
00300         frame->GetField(ID3FN_MIMETYPE)->Set(mimetype.c_str());
00301         frame->GetField(ID3FN_IMAGEFORMAT)->Set("");
00302         frame->GetField(ID3FN_PICTURETYPE)->Set(static_cast<unsigned int>(0));
00303         frame->GetField(ID3FN_DESCRIPTION)->Set("");
00304         frame->GetField(ID3FN_DATA)->Set(reinterpret_cast<const uchar*>(imgData.data()), imgData.size());
00305         tag.AttachFrame(frame);
00306       }
00307     }
00308   }
00309     
00310   //file.seekg(offsets[2]);
00311   //file.seekg(offsets[3]);
00312   dataWindow.setCur(offsets[4]);
00313     
00314   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_TITLE));
00315   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_ALBUM));
00316   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_LEADARTIST));
00317   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_CONTENTTYPE));
00318   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Tempo"));
00319   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Mood"));
00320   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Situation"));
00321   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Preference"));
00322   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_SONGLEN));
00323     
00324   // The next 12 bytes can be ignored.  The first 8 represent the 
00325   // creation date as a 64 bit floating point number.  The last 4 are
00326   // for a play counter.
00327   dataWindow.skipChars(12);
00328     
00329   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Path"));
00330   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Serial"));
00331     
00332   // 2 bytes for track
00333   uint32 trkNum = io::readLENumber(dataWindow, 2);
00334   if (trkNum > 0)
00335   {
00336     String trkStr = toString(trkNum);
00337     ID3_Frame* frame = new ID3_Frame(ID3FID_TRACKNUM);
00338     if (frame)
00339     {
00340       frame->GetField(ID3FN_TEXT)->Set(trkStr.c_str());
00341       tag.AttachFrame(frame);
00342     }
00343   }
00344     
00345   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Notes"));
00346   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Bio"));
00347   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_UNSYNCEDLYRICS));
00348   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWARTIST));
00349   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWCOMMERCIALINFO));
00350   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_ArtistEmail"));
00351     
00352   // email?
00353 
00354   return true;
00355 }

Generated at Sat Sep 8 15:51:10 2001 for id3lib by doxygen1.2.8 written by Dimitri van Heesch, © 1997-2001