src/tag_parse_musicmatch.cpp

Go to the documentation of this file.
00001 // $Id: tag_parse_musicmatch.cpp,v 1.19 2002/07/02 22:15:18 t1mpy 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 "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h"
00034 #include "helpers.h"
00035 #include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h"
00036 
00037 using namespace dami;
00038 
00039 namespace 
00040 {
00041   uint32 readSeconds(ID3_Reader& reader, size_t len)
00042   {
00043     io::ExitTrigger et(reader);
00044     io::WindowedReader wr(reader, len);
00045     ID3_Reader::pos_type beg = wr.getCur();
00046     uint32 seconds = 0;
00047     uint32 cur = 0;
00048     while (!wr.atEnd())
00049     {
00050       ID3_Reader::char_type ch = wr.readChar();
00051       if (':' == ch)
00052       {
00053         seconds += 60 * cur;
00054         cur = 0;
00055       }
00056       else if (!isdigit(ch))
00057       {
00058         return 0;
00059       }
00060       else
00061       {
00062         cur = cur * 10 + (ch - '0');
00063       }
00064     }
00065     et.release();
00066     return seconds + cur;
00067   }
00068   
00069   ID3_Frame* readTextFrame(ID3_Reader& reader, ID3_FrameID id, const String desc = "")
00070   {
00071     uint32 size = io::readLENumber(reader, 2);
00072     ID3D_NOTICE( "readTextFrame: size = " << size );
00073     if (size == 0)
00074     {
00075       return NULL;
00076     }
00077     
00078     String text;
00079     if (ID3FID_SONGLEN != id)
00080     {
00081       io::LineFeedReader lfr(reader);
00082       text = io::readText(lfr, size);
00083       ID3D_NOTICE( "readTextFrame: text = " << text );
00084     }
00085     else
00086     {
00087       text = toString(readSeconds(reader, size) * 1000);
00088       ID3D_NOTICE( "readTextFrame: songlen = " << text );
00089     }
00090     
00091     ID3_Frame* frame = new ID3_Frame(id);
00092     if (frame)
00093     {
00094       if (frame->Contains(ID3FN_TEXT))
00095       {
00096         frame->GetField(ID3FN_TEXT)->Set(text.c_str());
00097       }
00098       else if (frame->Contains(ID3FN_URL))
00099       {
00100         frame->GetField(ID3FN_URL)->Set(text.c_str());
00101       }
00102       if (frame->Contains(ID3FN_LANGUAGE))
00103       {
00104         frame->GetField(ID3FN_LANGUAGE)->Set("XXX");
00105       }
00106       if (frame->Contains(ID3FN_DESCRIPTION))
00107       {
00108         frame->GetField(ID3FN_DESCRIPTION)->Set(desc.c_str());
00109       }
00110     }
00111     return frame;
00112   }
00113 };
00114   
00115 bool mm::parse(ID3_TagImpl& tag, ID3_Reader& rdr)
00116 {
00117   io::ExitTrigger et(rdr);
00118   ID3_Reader::pos_type end = rdr.getCur();
00119   if (end < rdr.getBeg() + 48)
00120   {
00121     ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse, pos = " << end );
00122     return false;
00123   }
00124   
00125   rdr.setCur(end - 48);
00126   String version;
00127   
00128   {
00129     if (io::readText(rdr, 32) != "Brava Software Inc.             ")
00130     {
00131       ID3D_NOTICE( "mm::parse: bailing, couldn't find footer" );
00132       return false;
00133     }
00134     
00135     version = io::readText(rdr, 4);
00136     if (version.size() != 4 || 
00137         !isdigit(version[0]) || version[1] != '.' ||
00138         !isdigit(version[2]) || 
00139         !isdigit(version[3]))
00140     {
00141       ID3D_WARNING( "mm::parse: bailing, nonstandard version = " << version );
00142       return false;
00143     }
00144   }
00145     
00146   ID3_Reader::pos_type beg = rdr.setCur(end - 48);
00147   et.setExitPos(beg);
00148   if (end < 68)
00149   {
00150     ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse offsets, pos = " << end );
00151     return false;
00152   }
00153   rdr.setCur(end - 68);
00154     
00155   io::WindowedReader dataWindow(rdr);
00156   dataWindow.setEnd(rdr.getCur());
00157 
00158   uint32 offsets[5];
00159     
00160   io::WindowedReader offsetWindow(rdr, 20);
00161   for (size_t i = 0; i < 5; ++i)
00162   {
00163     offsets[i] = io::readLENumber(rdr, sizeof(uint32));
00164   }
00165 
00166   size_t metadataSize = 0;
00167   if (version <= "3.00")
00168   {
00169     // All MusicMatch tags up to and including version 3.0 had metadata 
00170     // sections exactly 7868 bytes in length.
00171     metadataSize = 7868;
00172   }
00173   else
00174   {
00175     // MusicMatch tags after version 3.0 had three possible lengths for their
00176     // metadata sections.  We can determine which it was by searching for
00177     // the version section signature that should precede the metadata section
00178     // by exactly 256 bytes.
00179     size_t possibleSizes[] = { 8132, 8004, 7936 };
00180       
00181     for (size_t i = 0; i < sizeof(possibleSizes)/sizeof(size_t); ++i)
00182     {
00183       dataWindow.setCur(dataWindow.getEnd());
00184         
00185       // Our offset will be exactly 256 bytes prior to our potential metadata
00186       // section
00187       size_t offset = possibleSizes[i] + 256;
00188       if (dataWindow.getCur() < offset)
00189       {
00190         // if our filesize is less than the offset, then it can't possibly
00191         // be the correct offset, so try again.
00192         continue;
00193       }
00194       dataWindow.setCur(dataWindow.getCur() - offset);
00195         
00196       // now read in the signature to see if it's a match
00197       if (io::readText(dataWindow, 8) == "18273645")
00198       {
00199         metadataSize = possibleSizes[i];
00200         break;
00201       }
00202     }
00203   }
00204   if (0 == metadataSize)
00205   {
00206     // if we didn't establish a size for the metadata, then something is
00207     // wrong.  probably should log this.
00208     ID3D_WARNING( "mm::parse: bailing, couldn't find meta data signature, end = " << end );
00209     return false;
00210   }
00211     
00212   // parse the offset pointers to determine the actual sizes of all the 
00213   // sections
00214   size_t sectionSizes[5];
00215   size_t tagSize = metadataSize;
00216     
00217   // we already know the size of the last section
00218   sectionSizes[4] = metadataSize;
00219     
00220   size_t lastOffset = 0;
00221   for (int i = 0; i < 5; i++)
00222   {
00223     size_t thisOffset = offsets[i];
00224     //ASSERT(thisOffset > lastOffset);
00225     if (i > 0)
00226     {
00227       size_t sectionSize = thisOffset - lastOffset;
00228       sectionSizes[i-1] = sectionSize;
00229       tagSize += sectionSize;
00230     }
00231     lastOffset = thisOffset;
00232   }
00233     
00234   // now check to see that our tag size is reasonable
00235   if (dataWindow.getEnd() < tagSize)
00236   {
00237     // Ack!  The tag size doesn't jive with the tag's ending position in
00238     // the file.  Bail!
00239     ID3D_WARNING( "mm::parse: bailing, tag size is too big, tag size = " << tagSize << ", end = " << end );
00240     return false;
00241   }
00242 
00243   dataWindow.setBeg(dataWindow.getEnd() - tagSize);
00244   dataWindow.setCur(dataWindow.getBeg());
00245     
00246   // Now calculate the adjusted offsets
00247   offsets[0] = dataWindow.getBeg();
00248   for (size_t i = 0; i < 4; ++i)
00249   {
00250     offsets[i+1] = offsets[i] + sectionSizes[i];
00251   }
00252     
00253   // now check for a tag header and adjust the tag_beg pointer appropriately
00254   if (dataWindow.getBeg() >= 256)
00255   {
00256     rdr.setCur(dataWindow.getBeg() - 256);
00257     if (io::readText(rdr, 8) == "18273645")
00258     {
00259       et.setExitPos(rdr.getCur() - 8);
00260     }
00261     else
00262     {
00263       et.setExitPos(dataWindow.getBeg());
00264     }
00265     dataWindow.setCur(dataWindow.getBeg());
00266   }
00267     
00268   // Now parse the various sections...
00269     
00270   // Parse the image extension at offset 0
00271   dataWindow.setCur(offsets[0]);
00272   String imgExt = io::readTrailingSpaces(dataWindow, 4);
00273     
00274   // Parse the image binary at offset 1
00275   dataWindow.setCur(offsets[1]);
00276   uint32 imgSize = io::readLENumber(dataWindow, 4);
00277   if (imgSize == 0)
00278   {
00279     // no image binary.  don't do anything.
00280   }
00281   else
00282   {
00283     io::WindowedReader imgWindow(dataWindow, imgSize);
00284     if (imgWindow.getEnd() < imgWindow.getBeg() + imgSize)
00285     {
00286       // Ack!  The image size given extends beyond the next offset!  This is 
00287       // not good...  log?
00288     }
00289     else
00290     {
00291       BString imgData = io::readAllBinary(imgWindow);
00292       ID3_Frame* frame = new ID3_Frame(ID3FID_PICTURE);
00293       if (frame)
00294       {
00295         String mimetype("image/");
00296         mimetype += imgExt;
00297         frame->GetField(ID3FN_MIMETYPE)->Set(mimetype.c_str());
00298         frame->GetField(ID3FN_IMAGEFORMAT)->Set("");
00299         frame->GetField(ID3FN_PICTURETYPE)->Set(static_cast<unsigned int>(0));
00300         frame->GetField(ID3FN_DESCRIPTION)->Set("");
00301         frame->GetField(ID3FN_DATA)->Set(reinterpret_cast<const uchar*>(imgData.data()), imgData.size());
00302         tag.AttachFrame(frame);
00303       }
00304     }
00305   }
00306     
00307   //file.seekg(offsets[2]);
00308   //file.seekg(offsets[3]);
00309   dataWindow.setCur(offsets[4]);
00310     
00311   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_TITLE));
00312   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_ALBUM));
00313   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_LEADARTIST));
00314   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_CONTENTTYPE));
00315   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Tempo"));
00316   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Mood"));
00317   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Situation"));
00318   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Preference"));
00319   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_SONGLEN));
00320     
00321   // The next 12 bytes can be ignored.  The first 8 represent the 
00322   // creation date as a 64 bit floating point number.  The last 4 are
00323   // for a play counter.
00324   dataWindow.skipChars(12);
00325     
00326   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Path"));
00327   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Serial"));
00328     
00329   // 2 bytes for track
00330   uint32 trkNum = io::readLENumber(dataWindow, 2);
00331   if (trkNum > 0)
00332   {
00333     String trkStr = toString(trkNum);
00334     ID3_Frame* frame = new ID3_Frame(ID3FID_TRACKNUM);
00335     if (frame)
00336     {
00337       frame->GetField(ID3FN_TEXT)->Set(trkStr.c_str());
00338       tag.AttachFrame(frame);
00339     }
00340   }
00341     
00342   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Notes"));
00343   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Bio"));
00344   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_UNSYNCEDLYRICS));
00345   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWARTIST));
00346   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWCOMMERCIALINFO));
00347   tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_ArtistEmail"));
00348     
00349   // email?
00350 
00351   return true;
00352 }
00353 

Generated on Mon Aug 20 17:48:46 2007 for id3lib by  doxygen 1.5.2