00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #if defined HAVE_CONFIG_H
00028 #include <config.h>
00029 #endif
00030
00031
00032 #include <ctype.h>
00033 #include "tag_impl.h"
00034 #include "helpers.h"
00035 #include "id3/io_decorators.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
00170
00171 metadataSize = 7868;
00172 }
00173 else
00174 {
00175
00176
00177
00178
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
00186
00187 size_t offset = possibleSizes[i] + 256;
00188 if (dataWindow.getCur() < offset)
00189 {
00190
00191
00192 continue;
00193 }
00194 dataWindow.setCur(dataWindow.getCur() - offset);
00195
00196
00197 if (io::readText(dataWindow, 8) == "18273645")
00198 {
00199 metadataSize = possibleSizes[i];
00200 break;
00201 }
00202 }
00203 }
00204 if (0 == metadataSize)
00205 {
00206
00207
00208 ID3D_WARNING( "mm::parse: bailing, couldn't find meta data signature, end = " << end );
00209 return false;
00210 }
00211
00212
00213
00214 size_t sectionSizes[5];
00215 size_t tagSize = metadataSize;
00216
00217
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
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
00235 if (dataWindow.getEnd() < tagSize)
00236 {
00237
00238
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
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
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
00269
00270
00271 dataWindow.setCur(offsets[0]);
00272 String imgExt = io::readTrailingSpaces(dataWindow, 4);
00273
00274
00275 dataWindow.setCur(offsets[1]);
00276 uint32 imgSize = io::readLENumber(dataWindow, 4);
00277 if (imgSize == 0)
00278 {
00279
00280 }
00281 else
00282 {
00283 io::WindowedReader imgWindow(dataWindow, imgSize);
00284 if (imgWindow.getEnd() < imgWindow.getBeg() + imgSize)
00285 {
00286
00287
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
00308
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
00322
00323
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
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
00350
00351 return true;
00352 }
00353