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