Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / mpeg / id3v2 / id3v2framefactory.cpp
1 /***************************************************************************
2     copyright            : (C) 2002 - 2008 by Scott Wheeler
3     email                : wheeler@kde.org
4  ***************************************************************************/
5
6 /***************************************************************************
7  *   This library is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU Lesser General Public License version   *
9  *   2.1 as published by the Free Software Foundation.                     *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful, but   *
12  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the Free Software   *
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
19  *   USA                                                                   *
20  *                                                                         *
21  *   Alternatively, this file is available under the Mozilla Public        *
22  *   License Version 1.1.  You may obtain a copy of the License at         *
23  *   http://www.mozilla.org/MPL/                                           *
24  ***************************************************************************/
25
26 #define HAVE_ZLIB 0
27
28 #ifndef HAVE_ZLIB
29 #include <config.h>
30 #endif
31
32 #include <tdebug.h>
33
34 #include "id3v2framefactory.h"
35 #include "id3v2synchdata.h"
36 #include "id3v1genres.h"
37
38 #include "frames/attachedpictureframe.h"
39 #include "frames/commentsframe.h"
40 #include "frames/relativevolumeframe.h"
41 #include "frames/textidentificationframe.h"
42 #include "frames/uniquefileidentifierframe.h"
43 #include "frames/unknownframe.h"
44 #include "frames/generalencapsulatedobjectframe.h"
45 #include "frames/urllinkframe.h"
46 #include "frames/unsynchronizedlyricsframe.h"
47 #include "frames/popularimeterframe.h"
48 #include "frames/privateframe.h"
49
50 using namespace TagLib;
51 using namespace ID3v2;
52
53 class FrameFactory::FrameFactoryPrivate
54 {
55 public:
56   FrameFactoryPrivate() :
57     defaultEncoding(String::Latin1),
58     useDefaultEncoding(false) {}
59
60   String::Type defaultEncoding;
61   bool useDefaultEncoding;
62
63   template <class T> void setTextEncoding(T *frame)
64   {
65     if(useDefaultEncoding)
66       frame->setTextEncoding(defaultEncoding);
67   }
68 };
69
70 FrameFactory *FrameFactory::factory = 0;
71
72 ////////////////////////////////////////////////////////////////////////////////
73 // public members
74 ////////////////////////////////////////////////////////////////////////////////
75
76 FrameFactory *FrameFactory::instance()
77 {
78   if(!factory)
79     factory = new FrameFactory;
80   return factory;
81 }
82
83 Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) const
84 {
85   return createFrame(data, uint(synchSafeInts ? 4 : 3));
86 }
87
88 Frame *FrameFactory::createFrame(const ByteVector &data, uint version) const
89 {
90   Header tagHeader;
91   tagHeader.setMajorVersion(version);
92   return createFrame(data, &tagHeader);
93 }
94
95 Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) const
96 {
97   ByteVector data = origData;
98   uint version = tagHeader->majorVersion();
99   Frame::Header *header = new Frame::Header(data, version);
100   ByteVector frameID = header->frameID();
101
102   // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
103   // characters.  Also make sure that there is data in the frame.
104
105   if(!frameID.size() == (version < 3 ? 3 : 4) ||
106      header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) ||
107      header->frameSize() > data.size())
108   {
109     delete header;
110     return 0;
111   }
112
113   for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
114     if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
115       delete header;
116       return 0;
117     }
118   }
119
120   if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) {
121     // Data lengths are not part of the encoded data, but since they are synch-safe
122     // integers they will be never actually encoded.
123     ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize());
124     frameData = SynchData::decode(frameData);
125     data = data.mid(0, Frame::Header::size(version)) + frameData;
126   }
127
128   // TagLib doesn't mess with encrypted frames, so just treat them
129   // as unknown frames.
130
131 #if HAVE_ZLIB == 0
132   if(header->compression()) {
133     debug("Compressed frames are currently not supported.");
134     return new UnknownFrame(data, header);
135   }
136 #endif
137   if(header->encryption()) {
138     debug("Encrypted frames are currently not supported.");
139     return new UnknownFrame(data, header);
140   }
141
142   if(!updateFrame(header)) {
143     header->setTagAlterPreservation(true);
144     return new UnknownFrame(data, header);
145   }
146
147   // updateFrame() might have updated the frame ID.
148
149   frameID = header->frameID();
150
151   // This is where things get necissarily nasty.  Here we determine which
152   // Frame subclass (or if none is found simply an Frame) based
153   // on the frame ID.  Since there are a lot of possibilities, that means
154   // a lot of if blocks.
155
156   // Text Identification (frames 4.2)
157
158   if(frameID.startsWith("T")) {
159
160     TextIdentificationFrame *f = frameID != "TXXX"
161       ? new TextIdentificationFrame(data, header)
162       : new UserTextIdentificationFrame(data, header);
163
164     d->setTextEncoding(f);
165
166     if(frameID == "TCON")
167       updateGenre(f);
168
169     return f;
170   }
171
172   // Comments (frames 4.10)
173
174   if(frameID == "COMM") {
175     CommentsFrame *f = new CommentsFrame(data, header);
176     d->setTextEncoding(f);
177     return f;
178   }
179
180   // Attached Picture (frames 4.14)
181
182   if(frameID == "APIC") {
183     AttachedPictureFrame *f = new AttachedPictureFrame(data, header);
184     d->setTextEncoding(f);
185     return f;
186   }
187
188   // ID3v2.2 Attached Picture
189
190         if(frameID == "PIC") {
191     AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header);
192     d->setTextEncoding(f);
193     return f;
194   }
195
196         // Relative Volume Adjustment (frames 4.11)
197
198   if(frameID == "RVA2")
199     return new RelativeVolumeFrame(data, header);
200
201   // Unique File Identifier (frames 4.1)
202
203   if(frameID == "UFID")
204     return new UniqueFileIdentifierFrame(data, header);
205
206   // General Encapsulated Object (frames 4.15)
207
208   if(frameID == "GEOB") {
209     GeneralEncapsulatedObjectFrame *f = new GeneralEncapsulatedObjectFrame(data, header);
210     d->setTextEncoding(f);
211     return f;
212   }
213
214   // URL link (frames 4.3)
215
216   if(frameID.startsWith("W")) {
217     if(frameID != "WXXX") {
218       return new UrlLinkFrame(data, header);
219     }
220     else {
221       UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
222       d->setTextEncoding(f);
223       return f;
224     }
225   }
226
227   // Unsynchronized lyric/text transcription (frames 4.8)
228
229   if(frameID == "USLT") {
230     UnsynchronizedLyricsFrame *f = new UnsynchronizedLyricsFrame(data, header);
231     if(d->useDefaultEncoding)
232       f->setTextEncoding(d->defaultEncoding);
233     return f;
234   }
235
236   // Popularimeter (frames 4.17)
237
238   if(frameID == "POPM")
239     return new PopularimeterFrame(data, header);
240
241   // Private (frames 4.27)
242
243   if(frameID == "PRIV")
244     return new PrivateFrame(data, header);
245
246   return new UnknownFrame(data, header);
247 }
248
249 String::Type FrameFactory::defaultTextEncoding() const
250 {
251   return d->defaultEncoding;
252 }
253
254 void FrameFactory::setDefaultTextEncoding(String::Type encoding)
255 {
256   d->useDefaultEncoding = true;
257   d->defaultEncoding = encoding;
258 }
259
260 ////////////////////////////////////////////////////////////////////////////////
261 // protected members
262 ////////////////////////////////////////////////////////////////////////////////
263
264 FrameFactory::FrameFactory()
265 {
266   d = new FrameFactoryPrivate;
267 }
268
269 FrameFactory::~FrameFactory()
270 {
271   delete d;
272 }
273
274 bool FrameFactory::updateFrame(Frame::Header *header) const
275 {
276   TagLib::ByteVector frameID = header->frameID();
277
278   switch(header->version()) {
279
280   case 2: // ID3v2.2
281   {
282     if(frameID == "CRM" ||
283        frameID == "EQU" ||
284        frameID == "LNK" ||
285        frameID == "RVA" ||
286        frameID == "TIM" ||
287        frameID == "TSI" ||
288        frameID == "TDA")
289     {
290       debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
291             ".  It will be discarded from the tag.");
292       return false;
293     }
294
295     // ID3v2.2 only used 3 bytes for the frame ID, so we need to convert all of
296     // the frames to their 4 byte ID3v2.4 equivalent.
297
298     convertFrame("BUF", "RBUF", header);
299     convertFrame("CNT", "PCNT", header);
300     convertFrame("COM", "COMM", header);
301     convertFrame("CRA", "AENC", header);
302     convertFrame("ETC", "ETCO", header);
303     convertFrame("GEO", "GEOB", header);
304     convertFrame("IPL", "TIPL", header);
305     convertFrame("MCI", "MCDI", header);
306     convertFrame("MLL", "MLLT", header);
307     convertFrame("POP", "POPM", header);
308     convertFrame("REV", "RVRB", header);
309     convertFrame("SLT", "SYLT", header);
310     convertFrame("STC", "SYTC", header);
311     convertFrame("TAL", "TALB", header);
312     convertFrame("TBP", "TBPM", header);
313     convertFrame("TCM", "TCOM", header);
314     convertFrame("TCO", "TCON", header);
315     convertFrame("TCR", "TCOP", header);
316     convertFrame("TDY", "TDLY", header);
317     convertFrame("TEN", "TENC", header);
318     convertFrame("TFT", "TFLT", header);
319     convertFrame("TKE", "TKEY", header);
320     convertFrame("TLA", "TLAN", header);
321     convertFrame("TLE", "TLEN", header);
322     convertFrame("TMT", "TMED", header);
323     convertFrame("TOA", "TOAL", header);
324     convertFrame("TOF", "TOFN", header);
325     convertFrame("TOL", "TOLY", header);
326     convertFrame("TOR", "TDOR", header);
327     convertFrame("TOT", "TOAL", header);
328     convertFrame("TP1", "TPE1", header);
329     convertFrame("TP2", "TPE2", header);
330     convertFrame("TP3", "TPE3", header);
331     convertFrame("TP4", "TPE4", header);
332     convertFrame("TPA", "TPOS", header);
333     convertFrame("TPB", "TPUB", header);
334     convertFrame("TRC", "TSRC", header);
335     convertFrame("TRD", "TDRC", header);
336     convertFrame("TRK", "TRCK", header);
337     convertFrame("TSS", "TSSE", header);
338     convertFrame("TT1", "TIT1", header);
339     convertFrame("TT2", "TIT2", header);
340     convertFrame("TT3", "TIT3", header);
341     convertFrame("TXT", "TOLY", header);
342     convertFrame("TXX", "TXXX", header);
343     convertFrame("TYE", "TDRC", header);
344     convertFrame("UFI", "UFID", header);
345     convertFrame("ULT", "USLT", header);
346     convertFrame("WAF", "WOAF", header);
347     convertFrame("WAR", "WOAR", header);
348     convertFrame("WAS", "WOAS", header);
349     convertFrame("WCM", "WCOM", header);
350     convertFrame("WCP", "WCOP", header);
351     convertFrame("WPB", "WPUB", header);
352     convertFrame("WXX", "WXXX", header);
353
354     break;
355   }
356
357   case 3: // ID3v2.3
358   {
359     if(frameID == "EQUA" ||
360        frameID == "RVAD" ||
361        frameID == "TIME" ||
362        frameID == "TRDA" ||
363        frameID == "TSIZ" ||
364        frameID == "TDAT")
365     {
366       debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
367             ".  It will be discarded from the tag.");
368       return false;
369     }
370
371     convertFrame("TORY", "TDOR", header);
372     convertFrame("TYER", "TDRC", header);
373
374     break;
375   }
376
377   default:
378
379     // This should catch a typo that existed in TagLib up to and including
380     // version 1.1 where TRDC was used for the year rather than TDRC.
381
382     convertFrame("TRDC", "TDRC", header);
383     break;
384   }
385
386   return true;
387 }
388
389 ////////////////////////////////////////////////////////////////////////////////
390 // private members
391 ////////////////////////////////////////////////////////////////////////////////
392
393 void FrameFactory::convertFrame(const char *from, const char *to,
394                                 Frame::Header *header) const
395 {
396   if(header->frameID() != from)
397     return;
398
399   // debug("ID3v2.4 no longer supports the frame type " + String(from) + "  It has" +
400   //       "been converted to the type " + String(to) + ".");
401
402   header->setFrameID(to);
403 }
404
405 void FrameFactory::updateGenre(TextIdentificationFrame *frame) const
406 {
407   StringList fields = frame->fieldList();
408   StringList newfields;
409
410   for(StringList::Iterator it = fields.begin(); it != fields.end(); ++it) {
411     String s = *it;
412     int end = s.find(")");
413
414     if(s.startsWith("(") && end > 0) {
415       // "(12)Genre"
416       String text = s.substr(end + 1);
417       bool ok;
418       int number = s.substr(1, end - 1).toInt(&ok);
419       if(ok && number >= 0 && number <= 255 && !(ID3v1::genre(number) == text))
420         newfields.append(s.substr(1, end - 1));
421       if(!text.isEmpty())
422         newfields.append(text);
423     }
424     else {
425       // "Genre" or "12"
426       newfields.append(s);
427     }
428   }
429
430   if(newfields.isEmpty())
431     fields.append(String::null);
432
433   frame->setText(newfields);
434
435 }