id3lib
3.8.3
|
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