id3lib  3.8.3
tag_parse_lyrics3.cpp
Go to the documentation of this file.
1 // $Id: tag_parse_lyrics3.cpp,v 1.35 2002/10/04 08:52:23 t1mpy Exp $
2 
3 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
4 // Copyright 1999, 2000 Scott Thomas Haug
5 // Copyright 2002 Thijmen Klok (thijmen@id3lib.org)
6 
7 // This library is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU Library General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or (at your
10 // option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU Library General Public License
18 // along with this library; if not, write to the Free Software Foundation,
19 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 
21 // The id3lib authors encourage improvements and optimisations to be sent to
22 // the id3lib coordinator. Please see the README file for details on where to
23 // send such submissions. See the AUTHORS file for a list of people who have
24 // contributed to id3lib. See the ChangeLog file for a list of changes to
25 // id3lib. These files are distributed with id3lib at
26 // http://download.sourceforge.net/id3lib/
27 
28 #include <ctype.h>
29 #include <memory.h>
30 #include "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h"
31 #include "helpers.h"
32 #include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h"
33 #include "io_strings.h"
34 
35 using namespace dami;
36 
37 namespace
38 {
39  uint32 readIntegerString(ID3_Reader& reader, size_t numBytes)
40  {
41  uint32 val = 0;
42  for (size_t i = 0; i < numBytes && isdigit(reader.peekChar()); ++i)
43  {
44  val = (val * 10) + (reader.readChar() - '0');
45  }
46  ID3D_NOTICE( "readIntegerString: val = " << val );
47  return val;
48  }
49 
50  uint32 readIntegerString(ID3_Reader& reader)
51  {
52  return readIntegerString(reader, reader.remainingBytes());
53  }
54 
55  bool isTimeStamp(ID3_Reader& reader)
56  {
57  ID3_Reader::pos_type cur = reader.getCur();
58  if (reader.getEnd() < cur + 7)
59  {
60  return false;
61  }
62  bool its = ('[' == reader.readChar() &&
63  isdigit(reader.readChar()) && isdigit(reader.readChar()) &&
64  ':' == reader.readChar() &&
65  isdigit(reader.readChar()) && isdigit(reader.readChar()) &&
66  ']' == reader.readChar());
67  reader.setCur(cur);
68  if (its)
69  {
70  ID3D_NOTICE( "isTimeStamp(): found timestamp, cur = " << reader.getCur() );
71  }
72  return its;
73  }
74 
75  uint32 readTimeStamp(ID3_Reader& reader)
76  {
77  reader.skipChars(1);
78  size_t sec = readIntegerString(reader, 2) * 60;
79  reader.skipChars(1);
80  sec += readIntegerString(reader, 2);
81  reader.skipChars(1);
82  ID3D_NOTICE( "readTimeStamp(): timestamp = " << sec );
83  return sec * 1000;
84  }
85 
86  bool findText(ID3_Reader& reader, String text)
87  {
88  if (text.empty())
89  {
90  return true;
91  }
92 
93  size_t index = 0;
94  while (!reader.atEnd())
95  {
96  ID3_Reader::char_type ch = reader.readChar();
97  if (ch == text[index])
98  {
99  index++;
100  }
101  else if (ch == text[0])
102  {
103  index = 1;
104  }
105  else
106  {
107  index = 0;
108  }
109  if (index == text.size())
110  {
111  reader.setCur(reader.getCur() - index);
112  ID3D_NOTICE( "findText: found \"" << text << "\" at " <<
113  reader.getCur() );
114  break;
115  }
116  }
117  return !reader.atEnd();
118  };
119 
120  void lyrics3ToSylt(ID3_Reader& reader, ID3_Writer& writer)
121  {
122  while (!reader.atEnd())
123  {
124  bool lf = false;
125  size_t ms = 0;
126  size_t count = 0;
127  while (isTimeStamp(reader))
128  {
129  // For now, just skip over multiple time stamps
130  if (count++ > 0)
131  {
132  readTimeStamp(reader);
133  }
134  else
135  {
136  ms = readTimeStamp(reader);
137  }
138  }
139  while (!reader.atEnd() && !isTimeStamp(reader))
140  {
141  ID3_Reader::char_type ch = reader.readChar();
142  if (0x0A == ch && (reader.atEnd() || isTimeStamp(reader)))
143  {
144  lf = true;
145  break;
146  }
147  else
148  {
149  writer.writeChar(ch);
150  }
151  }
152 
153  // put synch identifier
154  writer.writeChar('\0');
155 
156  // put timestamp
157  ID3D_NOTICE( "lyrics3toSylt: ms = " << ms );
158 
159  io::writeBENumber(writer, ms, sizeof(uint32));
160  if (lf)
161  {
162  ID3D_NOTICE( "lyrics3toSylt: adding lf" );
163 
164  // put the LF
165  writer.writeChar(0x0A);
166  }
167  }
168  }
169 };
170 
172 {
173  io::ExitTrigger et(reader);
174  ID3_Reader::pos_type end = reader.getCur();
175  if (end < reader.getBeg() + 9 + 128)
176  {
177  ID3D_NOTICE( "id3::v1::parse: bailing, not enough bytes to parse, pos = " << end );
178  return false;
179  }
180  reader.setCur(end - (9 + 128));
181 
182  {
183  if (io::readText(reader, 9) != "LYRICSEND" ||
184  io::readText(reader, 3) != "TAG")
185  {
186  return false;
187  }
188  }
189 
190  // we have a Lyrics3 v1.00 tag
191  if (end < reader.getBeg() + 11 + 9 + 128)
192  {
193  // the file size isn't large enough to actually hold lyrics
194  ID3D_WARNING( "id3::v1::parse: not enough data to parse lyrics3" );
195  return false;
196  }
197 
198  // reserve enough space for lyrics3 + id3v1 tag
199  size_t window = end - reader.getBeg();
200  size_t lyrDataSize = min<size_t>(window, 11 + 5100 + 9 + 128);
201  reader.setCur(end - lyrDataSize);
202  io::WindowedReader wr(reader, lyrDataSize - (9 + 128));
203 
204  if (!findText(wr, "LYRICSBEGIN"))
205  {
206  ID3D_WARNING( "id3::v1::parse: couldn't find LYRICSBEGIN, bailing" );
207  return false;
208  }
209 
210  et.setExitPos(wr.getCur());
211  wr.skipChars(11);
212  wr.setBeg(wr.getCur());
213 
214  io::LineFeedReader lfr(wr);
215  String lyrics = io::readText(lfr, wr.remainingBytes());
216  id3::v2::setLyrics(tag, lyrics, "Converted from Lyrics3 v1.00", "XXX");
217 
218  return true;
219 }
220 
221 //bool parse(TagImpl& tag, ID3_Reader& reader)
223 {
224  io::ExitTrigger et(reader);
225  ID3_Reader::pos_type end = reader.getCur();
226  if (end < reader.getBeg() + 6 + 9 + 128)
227  {
228  ID3D_NOTICE( "lyr3::v2::parse: bailing, not enough bytes to parse, pos = " << reader.getCur() );
229  return false;
230  }
231 
232  reader.setCur(end - (6 + 9 + 128));
233  uint32 lyrSize = 0;
234 
235  ID3_Reader::pos_type beg = reader.getCur();
236  lyrSize = readIntegerString(reader, 6);
237  if (reader.getCur() < beg + 6)
238  {
239  ID3D_NOTICE( "lyr3::v2::parse: couldn't find numeric string, lyrSize = " <<
240  lyrSize );
241  return false;
242  }
243 
244  if (io::readText(reader, 9) != "LYRICS200" ||
245  io::readText(reader, 3) != "TAG")
246  {
247  return false;
248  }
249 
250  if (end < reader.getBeg() + lyrSize + 6 + 9 + 128)
251  {
252  ID3D_WARNING( "lyr3::v2::parse: not enough data to parse tag, lyrSize = " << lyrSize );
253  return false;
254  }
255  reader.setCur(end - (lyrSize + 6 + 9 + 128));
256 
257  io::WindowedReader wr(reader);
258  wr.setWindow(wr.getCur(), lyrSize);
259 
260  beg = wr.getCur();
261 
262  if (io::readText(wr, 11) != "LYRICSBEGIN")
263  {
264  // not a lyrics v2.00 tag
265  ID3D_WARNING( "lyr3::v2::parse: couldn't find LYRICSBEGIN, bailing" );
266  return false;
267  }
268 
269  bool has_time_stamps = false;
270 
271  ID3_Frame* lyr_frame = NULL;
272 
273  while (!wr.atEnd())
274  {
275  uint32 fldSize;
276 
277  String fldName = io::readText(wr, 3);
278  ID3D_NOTICE( "lyr3::v2::parse: fldName = " << fldName );
279  fldSize = readIntegerString(wr, 5);
280  ID3D_NOTICE( "lyr3::v2::parse: fldSize = " << fldSize );
281 
282  String fldData;
283 
284  io::WindowedReader wr2(wr, fldSize);
285  io::LineFeedReader lfr(wr2);
286 
287  fldData = io::readText(lfr, fldSize);
288  ID3D_NOTICE( "lyr3::v2::parse: fldData = \"" << fldData << "\"" );
289 
290  // the IND field
291  if (fldName == "IND")
292  {
293  has_time_stamps = (fldData.size() > 1 && fldData[1] == '1');
294  }
295 
296  // the TITLE field
297  else if (fldName == "ETT" && !id3::v2::hasTitle(tag))
298  {
299  //tag.setTitle(fldData);
300  id3::v2::setTitle(tag, fldData);
301  }
302 
303  // the ARTIST field
304  else if (fldName == "EAR" && !id3::v2::hasArtist(tag))
305  {
306  //tag.setArtist(fldData);
307  id3::v2::setArtist(tag, fldData);
308  }
309 
310  // the ALBUM field
311  else if (fldName == "EAL" && !id3::v2::hasAlbum(tag))
312  {
313  //tag.setAlbum(fldData);
314  id3::v2::setAlbum(tag, fldData);
315  }
316 
317  // the Lyrics/Music AUTHOR field
318  else if (fldName == "AUT")
319  {
320  //tag.setAuthor(fldData);
321  id3::v2::setLyricist(tag, fldData);
322  }
323 
324  // the INFORMATION field
325  else if (fldName == "INF")
326  {
327  //tag.setInfo(fldData);
328  id3::v2::setComment(tag, fldData, "Lyrics3 v2.00 INF", "XXX");
329  }
330 
331  // the LYRICS field
332  else if (fldName == "LYR")
333  {
334  // if already found an INF field, use it as description
335  String desc = "Converted from Lyrics3 v2.00";
336  //tag.setLyrics(fldData);
337  if (!has_time_stamps)
338  {
339  lyr_frame = id3::v2::setLyrics(tag, fldData, desc, "XXX");
340  }
341  else
342  {
343  // converts from lyrics3 to SYLT in-place
344  io::StringReader sr(fldData);
345  ID3D_NOTICE( "lyr3::v2::parse: determining synced lyrics" );
346  BString sylt;
347  io::BStringWriter sw(sylt);
348  lyrics3ToSylt(sr, sw);
349 
350  lyr_frame = id3::v2::setSyncLyrics(tag, sylt, ID3TSF_MS, desc,
351  "XXX", ID3CT_LYRICS);
352  ID3D_NOTICE( "lyr3::v2::parse: determined synced lyrics" );
353  }
354  }
355  else if (fldName == "IMG")
356  {
357  // currently unsupported
358  ID3D_WARNING( "lyr3::v2::parse: IMG field unsupported" );
359  }
360  else
361  {
362  ID3D_WARNING( "lyr3::v2::parse: undefined field id: " <<
363  fldName );
364  }
365  }
366 
367  et.setExitPos(beg);
368  return true;
369 }
370 
The representative class of an id3v2 frame.
virtual size_type skipChars(size_type len)
Skip up to len chars in the stream and advance the internal position accordingly.
Definition: reader.h:97
virtual pos_type setCur(pos_type pos)=0
Set the value of the current position for reading.
virtual pos_type getCur()=0
Return the current position in the reader.
virtual bool atEnd()
Definition: reader.h:125
virtual int_type readChar()
Read a single character and advance the internal position.
Definition: reader.h:65
virtual pos_type getEnd()
Return the ending position in the reader.
Definition: reader.h:51
uint32 pos_type
Definition: reader.h:38
virtual size_type remainingBytes()
Definition: reader.h:109
uint8 char_type
Definition: reader.h:37
virtual pos_type getBeg()
Return the beginning position in the reader.
Definition: reader.h:48
virtual int_type peekChar()=0
Return the next character to be read without advancing the internal position.
virtual int_type writeChar(char_type ch)
Write a single character and advance the internal position.
Definition: writer.h:71
#define NULL
Definition: globals.h:743
@ ID3CT_LYRICS
Definition: globals.h:384
@ ID3TSF_MS
Definition: globals.h:420
ID3_C_EXPORT ID3_Frame * setLyricist(ID3_TagImpl &, String)
Definition: helpers.cpp:483
ID3_C_EXPORT ID3_Frame * setLyrics(ID3_TagImpl &, String, String, String)
Definition: helpers.cpp:439
ID3_C_EXPORT ID3_Frame * setAlbum(ID3_TagImpl &, String)
Definition: helpers.cpp:170
ID3_C_EXPORT ID3_Frame * setTitle(ID3_TagImpl &, String)
Definition: helpers.cpp:193
ID3_C_EXPORT ID3_Frame * setArtist(ID3_TagImpl &, String)
Definition: helpers.cpp:136
ID3_C_EXPORT ID3_Frame * hasArtist(const ID3_TagImpl &)
Definition: helpers.cpp:120
ID3_C_EXPORT ID3_Frame * setComment(ID3_TagImpl &, String, String, String)
Definition: helpers.cpp:258
ID3_C_EXPORT ID3_Frame * hasTitle(const ID3_TagImpl &)
Definition: helpers.cpp:182
ID3_C_EXPORT ID3_Frame * hasAlbum(const ID3_TagImpl &)
Definition: helpers.cpp:159
ID3_C_EXPORT ID3_Frame * setSyncLyrics(ID3_TagImpl &, BString, ID3_TimeStampFormat, String, String, ID3_ContentType)
Definition: helpers.cpp:503
bool parse(ID3_TagImpl &, ID3_Reader &)
bool parse(ID3_TagImpl &, ID3_Reader &)
Definition: tag_impl.h:42