id3lib  3.8.3
tag_parse_musicmatch.cpp
Go to the documentation of this file.
1 // $Id: tag_parse_musicmatch.cpp,v 1.19 2002/07/02 22:15:18 t1mpy Exp $
2 
3 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
4 // Copyright 1999, 2000 Scott Thomas Haug
5 
6 // This library is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU Library General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or (at your
9 // option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful, but WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14 // License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public License
17 // along with this library; if not, write to the Free Software Foundation,
18 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 
20 // The id3lib authors encourage improvements and optimisations to be sent to
21 // the id3lib coordinator. Please see the README file for details on where to
22 // send such submissions. See the AUTHORS file for a list of people who have
23 // contributed to id3lib. See the ChangeLog file for a list of changes to
24 // id3lib. These files are distributed with id3lib at
25 // http://download.sourceforge.net/id3lib/
26 
27 #if defined HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 
32 #include <ctype.h>
33 #include "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h"
34 #include "helpers.h"
35 #include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h"
36 
37 using namespace dami;
38 
39 namespace
40 {
41  uint32 readSeconds(ID3_Reader& reader, size_t len)
42  {
43  io::ExitTrigger et(reader);
44  io::WindowedReader wr(reader, len);
45  ID3_Reader::pos_type beg = wr.getCur();
46  uint32 seconds = 0;
47  uint32 cur = 0;
48  while (!wr.atEnd())
49  {
50  ID3_Reader::char_type ch = wr.readChar();
51  if (':' == ch)
52  {
53  seconds += 60 * cur;
54  cur = 0;
55  }
56  else if (!isdigit(ch))
57  {
58  return 0;
59  }
60  else
61  {
62  cur = cur * 10 + (ch - '0');
63  }
64  }
65  et.release();
66  return seconds + cur;
67  }
68 
69  ID3_Frame* readTextFrame(ID3_Reader& reader, ID3_FrameID id, const String desc = "")
70  {
71  uint32 size = io::readLENumber(reader, 2);
72  ID3D_NOTICE( "readTextFrame: size = " << size );
73  if (size == 0)
74  {
75  return NULL;
76  }
77 
78  String text;
79  if (ID3FID_SONGLEN != id)
80  {
81  io::LineFeedReader lfr(reader);
82  text = io::readText(lfr, size);
83  ID3D_NOTICE( "readTextFrame: text = " << text );
84  }
85  else
86  {
87  text = toString(readSeconds(reader, size) * 1000);
88  ID3D_NOTICE( "readTextFrame: songlen = " << text );
89  }
90 
91  ID3_Frame* frame = new ID3_Frame(id);
92  if (frame)
93  {
94  if (frame->Contains(ID3FN_TEXT))
95  {
96  frame->GetField(ID3FN_TEXT)->Set(text.c_str());
97  }
98  else if (frame->Contains(ID3FN_URL))
99  {
100  frame->GetField(ID3FN_URL)->Set(text.c_str());
101  }
102  if (frame->Contains(ID3FN_LANGUAGE))
103  {
104  frame->GetField(ID3FN_LANGUAGE)->Set("XXX");
105  }
106  if (frame->Contains(ID3FN_DESCRIPTION))
107  {
108  frame->GetField(ID3FN_DESCRIPTION)->Set(desc.c_str());
109  }
110  }
111  return frame;
112  }
113 };
114 
116 {
117  io::ExitTrigger et(rdr);
118  ID3_Reader::pos_type end = rdr.getCur();
119  if (end < rdr.getBeg() + 48)
120  {
121  ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse, pos = " << end );
122  return false;
123  }
124 
125  rdr.setCur(end - 48);
126  String version;
127 
128  {
129  if (io::readText(rdr, 32) != "Brava Software Inc. ")
130  {
131  ID3D_NOTICE( "mm::parse: bailing, couldn't find footer" );
132  return false;
133  }
134 
135  version = io::readText(rdr, 4);
136  if (version.size() != 4 ||
137  !isdigit(version[0]) || version[1] != '.' ||
138  !isdigit(version[2]) ||
139  !isdigit(version[3]))
140  {
141  ID3D_WARNING( "mm::parse: bailing, nonstandard version = " << version );
142  return false;
143  }
144  }
145 
146  ID3_Reader::pos_type beg = rdr.setCur(end - 48);
147  et.setExitPos(beg);
148  if (end < 68)
149  {
150  ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse offsets, pos = " << end );
151  return false;
152  }
153  rdr.setCur(end - 68);
154 
155  io::WindowedReader dataWindow(rdr);
156  dataWindow.setEnd(rdr.getCur());
157 
158  uint32 offsets[5];
159 
160  io::WindowedReader offsetWindow(rdr, 20);
161  for (size_t i = 0; i < 5; ++i)
162  {
163  offsets[i] = io::readLENumber(rdr, sizeof(uint32));
164  }
165 
166  size_t metadataSize = 0;
167  if (version <= "3.00")
168  {
169  // All MusicMatch tags up to and including version 3.0 had metadata
170  // sections exactly 7868 bytes in length.
171  metadataSize = 7868;
172  }
173  else
174  {
175  // MusicMatch tags after version 3.0 had three possible lengths for their
176  // metadata sections. We can determine which it was by searching for
177  // the version section signature that should precede the metadata section
178  // by exactly 256 bytes.
179  size_t possibleSizes[] = { 8132, 8004, 7936 };
180 
181  for (size_t i = 0; i < sizeof(possibleSizes)/sizeof(size_t); ++i)
182  {
183  dataWindow.setCur(dataWindow.getEnd());
184 
185  // Our offset will be exactly 256 bytes prior to our potential metadata
186  // section
187  size_t offset = possibleSizes[i] + 256;
188  if (dataWindow.getCur() < offset)
189  {
190  // if our filesize is less than the offset, then it can't possibly
191  // be the correct offset, so try again.
192  continue;
193  }
194  dataWindow.setCur(dataWindow.getCur() - offset);
195 
196  // now read in the signature to see if it's a match
197  if (io::readText(dataWindow, 8) == "18273645")
198  {
199  metadataSize = possibleSizes[i];
200  break;
201  }
202  }
203  }
204  if (0 == metadataSize)
205  {
206  // if we didn't establish a size for the metadata, then something is
207  // wrong. probably should log this.
208  ID3D_WARNING( "mm::parse: bailing, couldn't find meta data signature, end = " << end );
209  return false;
210  }
211 
212  // parse the offset pointers to determine the actual sizes of all the
213  // sections
214  size_t sectionSizes[5];
215  size_t tagSize = metadataSize;
216 
217  // we already know the size of the last section
218  sectionSizes[4] = metadataSize;
219 
220  size_t lastOffset = 0;
221  for (int i = 0; i < 5; i++)
222  {
223  size_t thisOffset = offsets[i];
224  //ASSERT(thisOffset > lastOffset);
225  if (i > 0)
226  {
227  size_t sectionSize = thisOffset - lastOffset;
228  sectionSizes[i-1] = sectionSize;
229  tagSize += sectionSize;
230  }
231  lastOffset = thisOffset;
232  }
233 
234  // now check to see that our tag size is reasonable
235  if (dataWindow.getEnd() < tagSize)
236  {
237  // Ack! The tag size doesn't jive with the tag's ending position in
238  // the file. Bail!
239  ID3D_WARNING( "mm::parse: bailing, tag size is too big, tag size = " << tagSize << ", end = " << end );
240  return false;
241  }
242 
243  dataWindow.setBeg(dataWindow.getEnd() - tagSize);
244  dataWindow.setCur(dataWindow.getBeg());
245 
246  // Now calculate the adjusted offsets
247  offsets[0] = dataWindow.getBeg();
248  for (size_t i = 0; i < 4; ++i)
249  {
250  offsets[i+1] = offsets[i] + sectionSizes[i];
251  }
252 
253  // now check for a tag header and adjust the tag_beg pointer appropriately
254  if (dataWindow.getBeg() >= 256)
255  {
256  rdr.setCur(dataWindow.getBeg() - 256);
257  if (io::readText(rdr, 8) == "18273645")
258  {
259  et.setExitPos(rdr.getCur() - 8);
260  }
261  else
262  {
263  et.setExitPos(dataWindow.getBeg());
264  }
265  dataWindow.setCur(dataWindow.getBeg());
266  }
267 
268  // Now parse the various sections...
269 
270  // Parse the image extension at offset 0
271  dataWindow.setCur(offsets[0]);
272  String imgExt = io::readTrailingSpaces(dataWindow, 4);
273 
274  // Parse the image binary at offset 1
275  dataWindow.setCur(offsets[1]);
276  uint32 imgSize = io::readLENumber(dataWindow, 4);
277  if (imgSize == 0)
278  {
279  // no image binary. don't do anything.
280  }
281  else
282  {
283  io::WindowedReader imgWindow(dataWindow, imgSize);
284  if (imgWindow.getEnd() < imgWindow.getBeg() + imgSize)
285  {
286  // Ack! The image size given extends beyond the next offset! This is
287  // not good... log?
288  }
289  else
290  {
291  BString imgData = io::readAllBinary(imgWindow);
292  ID3_Frame* frame = new ID3_Frame(ID3FID_PICTURE);
293  if (frame)
294  {
295  String mimetype("image/");
296  mimetype += imgExt;
297  frame->GetField(ID3FN_MIMETYPE)->Set(mimetype.c_str());
298  frame->GetField(ID3FN_IMAGEFORMAT)->Set("");
299  frame->GetField(ID3FN_PICTURETYPE)->Set(static_cast<unsigned int>(0));
300  frame->GetField(ID3FN_DESCRIPTION)->Set("");
301  frame->GetField(ID3FN_DATA)->Set(reinterpret_cast<const uchar*>(imgData.data()), imgData.size());
302  tag.AttachFrame(frame);
303  }
304  }
305  }
306 
307  //file.seekg(offsets[2]);
308  //file.seekg(offsets[3]);
309  dataWindow.setCur(offsets[4]);
310 
311  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_TITLE));
312  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_ALBUM));
313  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_LEADARTIST));
314  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_CONTENTTYPE));
315  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Tempo"));
316  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Mood"));
317  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Situation"));
318  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Preference"));
319  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_SONGLEN));
320 
321  // The next 12 bytes can be ignored. The first 8 represent the
322  // creation date as a 64 bit floating point number. The last 4 are
323  // for a play counter.
324  dataWindow.skipChars(12);
325 
326  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Path"));
327  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Serial"));
328 
329  // 2 bytes for track
330  uint32 trkNum = io::readLENumber(dataWindow, 2);
331  if (trkNum > 0)
332  {
333  String trkStr = toString(trkNum);
334  ID3_Frame* frame = new ID3_Frame(ID3FID_TRACKNUM);
335  if (frame)
336  {
337  frame->GetField(ID3FN_TEXT)->Set(trkStr.c_str());
338  tag.AttachFrame(frame);
339  }
340  }
341 
342  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Notes"));
343  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Bio"));
344  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_UNSYNCEDLYRICS));
345  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWARTIST));
346  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWCOMMERCIALINFO));
347  tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_ArtistEmail"));
348 
349  // email?
350 
351  return true;
352 }
353 
The representative class of an id3v2 frame.
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.
uint32 pos_type
Definition: reader.h:38
uint8 char_type
Definition: reader.h:37
virtual pos_type getBeg()
Return the beginning position in the reader.
Definition: reader.h:48
bool AttachFrame(ID3_Frame *)
Definition: tag_impl.cpp:167
#define NULL
Definition: globals.h:743
@ ID3FN_MIMETYPE
Mimetype field.
Definition: globals.h:212
@ ID3FN_DESCRIPTION
Description field.
Definition: globals.h:204
@ ID3FN_IMAGEFORMAT
Image format field.
Definition: globals.h:211
@ ID3FN_LANGUAGE
Language field.
Definition: globals.h:209
@ ID3FN_TEXT
Text field.
Definition: globals.h:201
@ ID3FN_URL
A URL.
Definition: globals.h:202
@ ID3FN_DATA
Data field.
Definition: globals.h:203
@ ID3FN_PICTURETYPE
Picture type field.
Definition: globals.h:210
unsigned char uchar
Definition: globals.h:114
ID3_FrameID
Enumeration of the different types of frames recognized by id3lib.
Definition: globals.h:230
@ ID3FID_LEADARTIST
Lead performer(s)/Soloist(s)
Definition: globals.h:292
@ ID3FID_CONTENTTYPE
Content type.
Definition: globals.h:263
@ ID3FID_WWWARTIST
Official artist/performer webpage.
Definition: globals.h:318
@ ID3FID_ALBUM
Album/Movie/Show title.
Definition: globals.h:260
@ ID3FID_COMMENT
Comments.
Definition: globals.h:235
@ ID3FID_SONGLEN
Length.
Definition: globals.h:282
@ ID3FID_WWWCOMMERCIALINFO
Commercial information.
Definition: globals.h:315
@ ID3FID_PICTURE
Attached picture.
Definition: globals.h:233
@ ID3FID_TRACKNUM
Track number/Position in set.
Definition: globals.h:299
@ ID3FID_UNSYNCEDLYRICS
Unsynchronized lyric/text transcription.
Definition: globals.h:314
@ ID3FID_TITLE
Title/songname/content description.
Definition: globals.h:278
bool parse(ID3_TagImpl &, ID3_Reader &)
Definition: tag_impl.h:42
String ID3_C_EXPORT toString(uint32 val)
Definition: utils.cpp:365