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