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
00028
#include <ctype.h>
00029
#include <memory.h>
00030
#include "tag_impl.h"
00031
#include "helpers.h"
00032
#include "id3/io_decorators.h"
00033
#include "io_strings.h"
00034
00035
using namespace dami;
00036
00037
namespace
00038
{
00039 uint32 readIntegerString(
ID3_Reader& reader, size_t numBytes)
00040 {
00041 uint32 val = 0;
00042
for (size_t i = 0; i < numBytes && isdigit(reader.
peekChar()); ++i)
00043 {
00044 val = (val * 10) + (reader.
readChar() -
'0');
00045 }
00046 ID3D_NOTICE(
"readIntegerString: val = " << val );
00047
return val;
00048 }
00049
00050 uint32 readIntegerString(
ID3_Reader& reader)
00051 {
00052
return readIntegerString(reader, reader.
remainingBytes());
00053 }
00054
00055
bool isTimeStamp(
ID3_Reader& reader)
00056 {
00057
ID3_Reader::pos_type cur = reader.
getCur();
00058
if (reader.
getEnd() < cur + 7)
00059 {
00060
return false;
00061 }
00062
bool its = (
'[' == reader.
readChar() &&
00063 isdigit(reader.
readChar()) && isdigit(reader.
readChar()) &&
00064
':' == reader.
readChar() &&
00065 isdigit(reader.
readChar()) && isdigit(reader.
readChar()) &&
00066
']' == reader.
readChar());
00067 reader.
setCur(cur);
00068
if (its)
00069 {
00070 ID3D_NOTICE(
"isTimeStamp(): found timestamp, cur = " << reader.
getCur() );
00071 }
00072
return its;
00073 }
00074
00075 uint32 readTimeStamp(
ID3_Reader& reader)
00076 {
00077 reader.
skipChars(1);
00078 size_t sec = readIntegerString(reader, 2) * 60;
00079 reader.
skipChars(1);
00080 sec += readIntegerString(reader, 2);
00081 reader.
skipChars(1);
00082 ID3D_NOTICE(
"readTimeStamp(): timestamp = " << sec );
00083
return sec * 1000;
00084 }
00085
00086
bool findText(
ID3_Reader& reader, String text)
00087 {
00088
if (text.empty())
00089 {
00090
return true;
00091 }
00092
00093 size_t index = 0;
00094
while (!reader.
atEnd())
00095 {
00096
ID3_Reader::char_type ch = reader.
readChar();
00097
if (ch == text[index])
00098 {
00099 index++;
00100 }
00101
else if (ch == text[0])
00102 {
00103 index = 1;
00104 }
00105
else
00106 {
00107 index = 0;
00108 }
00109
if (index == text.size())
00110 {
00111 reader.
setCur(reader.
getCur() - index);
00112 ID3D_NOTICE(
"findText: found \"" << text <<
"\" at " <<
00113 reader.
getCur() );
00114
break;
00115 }
00116 }
00117
return !reader.
atEnd();
00118 };
00119
00120
void lyrics3ToSylt(
ID3_Reader& reader,
ID3_Writer& writer)
00121 {
00122
while (!reader.
atEnd())
00123 {
00124
bool lf =
false;
00125 size_t ms = 0;
00126 size_t count = 0;
00127
while (isTimeStamp(reader))
00128 {
00129
00130
if (count++ > 0)
00131 {
00132 readTimeStamp(reader);
00133 }
00134
else
00135 {
00136 ms = readTimeStamp(reader);
00137 }
00138 }
00139
while (!reader.
atEnd() && !isTimeStamp(reader))
00140 {
00141
ID3_Reader::char_type ch = reader.
readChar();
00142
if (0x0A == ch && (reader.
atEnd() || isTimeStamp(reader)))
00143 {
00144 lf =
true;
00145
break;
00146 }
00147
else
00148 {
00149 writer.
writeChar(ch);
00150 }
00151 }
00152
00153
00154 writer.
writeChar(
'\0');
00155
00156
00157 ID3D_NOTICE(
"lyrics3toSylt: ms = " << ms );
00158
00159 io::writeBENumber(writer, ms,
sizeof(uint32));
00160
if (lf)
00161 {
00162 ID3D_NOTICE(
"lyrics3toSylt: adding lf" );
00163
00164
00165 writer.
writeChar(0x0A);
00166 }
00167 }
00168 }
00169 };
00170
00171
bool lyr3::v1::parse(
ID3_TagImpl& tag,
ID3_Reader& reader)
00172 {
00173
io::ExitTrigger et(reader);
00174
ID3_Reader::pos_type end = reader.
getCur();
00175
if (end < reader.
getBeg() + 9 + 128)
00176 {
00177 ID3D_NOTICE(
"id3::v1::parse: bailing, not enough bytes to parse, pos = " << end );
00178
return false;
00179 }
00180 reader.
setCur(end - (9 + 128));
00181
00182 {
00183
if (io::readText(reader, 9) !=
"LYRICSEND" ||
00184 io::readText(reader, 3) !=
"TAG")
00185 {
00186
return false;
00187 }
00188 }
00189
00190
00191
if (end < reader.
getBeg() + 11 + 9 + 128)
00192 {
00193
00194 ID3D_WARNING(
"id3::v1::parse: not enough data to parse lyrics3" );
00195
return false;
00196 }
00197
00198
00199 size_t window = end - reader.
getBeg();
00200 size_t lyrDataSize = min<size_t>(window, 11 + 5100 + 9 + 128);
00201 reader.
setCur(end - lyrDataSize);
00202
io::WindowedReader wr(reader, lyrDataSize - (9 + 128));
00203
00204
if (!findText(wr,
"LYRICSBEGIN"))
00205 {
00206 ID3D_WARNING(
"id3::v1::parse: couldn't find LYRICSBEGIN, bailing" );
00207
return false;
00208 }
00209
00210 et.
setExitPos(wr.
getCur());
00211 wr.
skipChars(11);
00212 wr.
setBeg(wr.
getCur());
00213
00214
io::LineFeedReader lfr(wr);
00215 String lyrics = io::readText(lfr, wr.
remainingBytes());
00216 id3::v2::setLyrics(tag, lyrics,
"Converted from Lyrics3 v1.00",
"XXX");
00217
00218
return true;
00219 }
00220
00221
00222
bool lyr3::v2::parse(
ID3_TagImpl& tag,
ID3_Reader& reader)
00223 {
00224
io::ExitTrigger et(reader);
00225
ID3_Reader::pos_type end = reader.
getCur();
00226
if (end < reader.
getBeg() + 6 + 9 + 128)
00227 {
00228 ID3D_NOTICE(
"lyr3::v2::parse: bailing, not enough bytes to parse, pos = " << reader.
getCur() );
00229
return false;
00230 }
00231
00232 reader.
setCur(end - (6 + 9 + 128));
00233 uint32 lyrSize = 0;
00234
00235
ID3_Reader::pos_type beg = reader.
getCur();
00236 lyrSize = readIntegerString(reader, 6);
00237
if (reader.
getCur() < beg + 6)
00238 {
00239 ID3D_NOTICE(
"lyr3::v2::parse: couldn't find numeric string, lyrSize = " <<
00240 lyrSize );
00241
return false;
00242 }
00243
00244
if (io::readText(reader, 9) !=
"LYRICS200" ||
00245 io::readText(reader, 3) !=
"TAG")
00246 {
00247
return false;
00248 }
00249
00250
if (end < reader.
getBeg() + lyrSize + 6 + 9 + 128)
00251 {
00252 ID3D_WARNING(
"lyr3::v2::parse: not enough data to parse tag, lyrSize = " << lyrSize );
00253
return false;
00254 }
00255 reader.
setCur(end - (lyrSize + 6 + 9 + 128));
00256
00257
io::WindowedReader wr(reader);
00258 wr.
setWindow(wr.
getCur(), lyrSize);
00259
00260 beg = wr.
getCur();
00261
00262
if (io::readText(wr, 11) !=
"LYRICSBEGIN")
00263 {
00264
00265 ID3D_WARNING(
"lyr3::v2::parse: couldn't find LYRICSBEGIN, bailing" );
00266
return false;
00267 }
00268
00269
bool has_time_stamps =
false;
00270
00271
ID3_Frame* lyr_frame =
NULL;
00272
00273
while (!wr.
atEnd())
00274 {
00275 uint32 fldSize;
00276
00277 String fldName = io::readText(wr, 3);
00278 ID3D_NOTICE(
"lyr3::v2::parse: fldName = " << fldName );
00279 fldSize = readIntegerString(wr, 5);
00280 ID3D_NOTICE(
"lyr3::v2::parse: fldSize = " << fldSize );
00281
00282 String fldData;
00283
00284
io::WindowedReader wr2(wr, fldSize);
00285
io::LineFeedReader lfr(wr2);
00286
00287 fldData = io::readText(lfr, fldSize);
00288 ID3D_NOTICE(
"lyr3::v2::parse: fldData = \"" << fldData <<
"\"" );
00289
00290
00291
if (fldName ==
"IND")
00292 {
00293 has_time_stamps = (fldData.size() > 1 && fldData[1] ==
'1');
00294 }
00295
00296
00297
else if (fldName ==
"ETT" && !id3::v2::hasTitle(tag))
00298 {
00299
00300 id3::v2::setTitle(tag, fldData);
00301 }
00302
00303
00304
else if (fldName ==
"EAR" && !id3::v2::hasArtist(tag))
00305 {
00306
00307 id3::v2::setArtist(tag, fldData);
00308 }
00309
00310
00311
else if (fldName ==
"EAL" && !id3::v2::hasAlbum(tag))
00312 {
00313
00314 id3::v2::setAlbum(tag, fldData);
00315 }
00316
00317
00318
else if (fldName ==
"AUT")
00319 {
00320
00321 id3::v2::setLyricist(tag, fldData);
00322 }
00323
00324
00325
else if (fldName ==
"INF")
00326 {
00327
00328 id3::v2::setComment(tag, fldData,
"Lyrics3 v2.00 INF",
"XXX");
00329 }
00330
00331
00332
else if (fldName ==
"LYR")
00333 {
00334
00335 String desc =
"Converted from Lyrics3 v2.00";
00336
00337
if (!has_time_stamps)
00338 {
00339 lyr_frame = id3::v2::setLyrics(tag, fldData, desc,
"XXX");
00340 }
00341
else
00342 {
00343
00344
io::StringReader sr(fldData);
00345 ID3D_NOTICE(
"lyr3::v2::parse: determining synced lyrics" );
00346 BString sylt;
00347
io::BStringWriter sw(sylt);
00348 lyrics3ToSylt(sr, sw);
00349
00350 lyr_frame = id3::v2::setSyncLyrics(tag, sylt, ID3TSF_MS, desc,
00351
"XXX", ID3CT_LYRICS);
00352 ID3D_NOTICE(
"lyr3::v2::parse: determined synced lyrics" );
00353 }
00354 }
00355
else if (fldName ==
"IMG")
00356 {
00357
00358 ID3D_WARNING(
"lyr3::v2::parse: IMG field unsupported" );
00359 }
00360
else
00361 {
00362 ID3D_WARNING(
"lyr3::v2::parse: undefined field id: " <<
00363 fldName );
00364 }
00365 }
00366
00367 et.
setExitPos(beg);
00368
return true;
00369 }
00370