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 <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
00173
00174 metadataSize = 7868;
00175 }
00176 else
00177 {
00178
00179
00180
00181
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
00189
00190 size_t offset = possibleSizes[i] + 256;
00191 if (dataWindow.getCur() < offset)
00192 {
00193
00194
00195 continue;
00196 }
00197 dataWindow.setCur(dataWindow.getCur() - offset);
00198
00199
00200 if (io::readText(dataWindow, 8) == "18273645")
00201 {
00202 metadataSize = possibleSizes[i];
00203 break;
00204 }
00205 }
00206 }
00207 if (0 == metadataSize)
00208 {
00209
00210
00211 ID3D_WARNING( "mm::parse: bailing, couldn't find meta data signature, end = " << end );
00212 return false;
00213 }
00214
00215
00216
00217 size_t sectionSizes[5];
00218 size_t tagSize = metadataSize;
00219
00220
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
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
00238 if (dataWindow.getEnd() < tagSize)
00239 {
00240
00241
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
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
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
00272
00273
00274 dataWindow.setCur(offsets[0]);
00275 String imgExt = io::readTrailingSpaces(dataWindow, 4);
00276
00277
00278 dataWindow.setCur(offsets[1]);
00279 uint32 imgSize = io::readLENumber(dataWindow, 4);
00280 if (imgSize == 0)
00281 {
00282
00283 }
00284 else
00285 {
00286 io::WindowedReader imgWindow(dataWindow, imgSize);
00287 if (imgWindow.getEnd() < imgWindow.getBeg() + imgSize)
00288 {
00289
00290
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
00311
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
00325
00326
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
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
00353
00354 return true;
00355 }