Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / mpeg / mpegfile.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 #include <tagunion.h>
27 #include <id3v2tag.h>
28 #include <id3v2header.h>
29 #include <id3v1tag.h>
30 #include <apefooter.h>
31 #include <apetag.h>
32 #include <tdebug.h>
33
34 #include <bitset>
35
36 #include "mpegfile.h"
37 #include "mpegheader.h"
38
39 using namespace TagLib;
40
41 namespace
42 {
43   enum { ID3v2Index = 0, APEIndex = 1, ID3v1Index = 2 };
44 }
45
46 class MPEG::File::FilePrivate
47 {
48 public:
49   FilePrivate(ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
50     ID3v2FrameFactory(frameFactory),
51     ID3v2Location(-1),
52     ID3v2OriginalSize(0),
53     APELocation(-1),
54     APEFooterLocation(-1),
55     APEOriginalSize(0),
56     ID3v1Location(-1),
57     hasID3v2(false),
58     hasID3v1(false),
59     hasAPE(false),
60     properties(0)
61   {
62
63   }
64
65   ~FilePrivate()
66   {
67     delete properties;
68   }
69
70   const ID3v2::FrameFactory *ID3v2FrameFactory;
71
72   long ID3v2Location;
73   uint ID3v2OriginalSize;
74
75   long APELocation;
76   long APEFooterLocation;
77   uint APEOriginalSize;
78
79   long ID3v1Location;
80
81   TagUnion tag;
82
83   // These indicate whether the file *on disk* has these tags, not if
84   // this data structure does.  This is used in computing offsets.
85
86   bool hasID3v2;
87   bool hasID3v1;
88   bool hasAPE;
89
90   Properties *properties;
91 };
92
93 ////////////////////////////////////////////////////////////////////////////////
94 // public members
95 ////////////////////////////////////////////////////////////////////////////////
96
97 MPEG::File::File(FileName file, bool readProperties,
98                  Properties::ReadStyle propertiesStyle) : TagLib::File(file)
99 {
100   d = new FilePrivate;
101
102   if(isOpen())
103     read(readProperties, propertiesStyle);
104 }
105
106 MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
107                  bool readProperties, Properties::ReadStyle propertiesStyle) :
108   TagLib::File(file)
109 {
110   d = new FilePrivate(frameFactory);
111
112   if(isOpen())
113     read(readProperties, propertiesStyle);
114 }
115
116 MPEG::File::~File()
117 {
118   delete d;
119 }
120
121 TagLib::Tag *MPEG::File::tag() const
122 {
123   return &d->tag;
124 }
125
126 MPEG::Properties *MPEG::File::audioProperties() const
127 {
128   return d->properties;
129 }
130
131 bool MPEG::File::save()
132 {
133   return save(AllTags);
134 }
135
136 bool MPEG::File::save(int tags)
137 {
138   return save(tags, true);
139 }
140
141 bool MPEG::File::save(int tags, bool stripOthers)
142 {
143   if(tags == NoTags && stripOthers)
144     return strip(AllTags);
145
146   if(!ID3v2Tag() && !ID3v1Tag() && !APETag()) {
147
148     if((d->hasID3v1 || d->hasID3v2 || d->hasAPE) && stripOthers)
149       return strip(AllTags);
150
151     return true;
152   }
153
154   if(readOnly()) {
155     debug("MPEG::File::save() -- File is read only.");
156     return false;
157   }
158
159   // Create the tags if we've been asked to.  Copy the values from the tag that
160   // does exist into the new tag.
161
162   if((tags & ID3v2) && ID3v1Tag())
163     Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
164
165   if((tags & ID3v1) && d->tag[ID3v2Index])
166     Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
167
168   bool success = true;
169
170   if(ID3v2 & tags) {
171
172     if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
173
174       if(!d->hasID3v2)
175         d->ID3v2Location = 0;
176
177       insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
178
179       d->hasID3v2 = true;
180
181       // v1 tag location has changed, update if it exists
182
183       if(ID3v1Tag())
184         d->ID3v1Location = findID3v1();
185
186       // APE tag location has changed, update if it exists
187
188       if(APETag())
189         findAPE();
190     }
191     else if(stripOthers)
192       success = strip(ID3v2, false) && success;
193   }
194   else if(d->hasID3v2 && stripOthers)
195     success = strip(ID3v2) && success;
196
197   if(ID3v1 & tags) {
198     if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
199       int offset = d->hasID3v1 ? -128 : 0;
200       seek(offset, End);
201       writeBlock(ID3v1Tag()->render());
202       d->hasID3v1 = true;
203       d->ID3v1Location = findID3v1();
204     }
205     else if(stripOthers)
206       success = strip(ID3v1) && success;
207   }
208   else if(d->hasID3v1 && stripOthers)
209     success = strip(ID3v1, false) && success;
210
211   // Dont save an APE-tag unless one has been created
212
213   if((APE & tags) && APETag()) {
214     if(d->hasAPE)
215       insert(APETag()->render(), d->APELocation, d->APEOriginalSize);
216     else {
217       if(d->hasID3v1) {
218         insert(APETag()->render(), d->ID3v1Location, 0);
219         d->APEOriginalSize = APETag()->footer()->completeTagSize();
220         d->hasAPE = true;
221         d->APELocation = d->ID3v1Location;
222         d->ID3v1Location += d->APEOriginalSize;
223       }
224       else {
225         seek(0, End);
226         d->APELocation = tell();
227         d->APEFooterLocation = d->APELocation
228           + d->tag.access<APE::Tag>(APEIndex, false)->footer()->completeTagSize()
229           - APE::Footer::size();
230         writeBlock(APETag()->render());
231         d->APEOriginalSize = APETag()->footer()->completeTagSize();
232         d->hasAPE = true;
233       }
234     }
235   }
236   else if(d->hasAPE && stripOthers)
237     success = strip(APE, false) && success;
238
239   return success;
240 }
241
242 ID3v2::Tag *MPEG::File::ID3v2Tag(bool create)
243 {
244   return d->tag.access<ID3v2::Tag>(ID3v2Index, create);
245 }
246
247 ID3v1::Tag *MPEG::File::ID3v1Tag(bool create)
248 {
249   return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
250 }
251
252 APE::Tag *MPEG::File::APETag(bool create)
253 {
254   return d->tag.access<APE::Tag>(APEIndex, create);
255 }
256
257 bool MPEG::File::strip(int tags)
258 {
259   return strip(tags, true);
260 }
261
262 bool MPEG::File::strip(int tags, bool freeMemory)
263 {
264   if(readOnly()) {
265     debug("MPEG::File::strip() - Cannot strip tags from a read only file.");
266     return false;
267   }
268
269   if((tags & ID3v2) && d->hasID3v2) {
270     removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
271     d->ID3v2Location = -1;
272     d->ID3v2OriginalSize = 0;
273     d->hasID3v2 = false;
274
275     if(freeMemory)
276       d->tag.set(ID3v2Index, 0);
277
278     // v1 tag location has changed, update if it exists
279
280     if(ID3v1Tag())
281       d->ID3v1Location = findID3v1();
282
283     // APE tag location has changed, update if it exists
284
285    if(APETag())
286       findAPE();
287   }
288
289   if((tags & ID3v1) && d->hasID3v1) {
290     truncate(d->ID3v1Location);
291     d->ID3v1Location = -1;
292     d->hasID3v1 = false;
293
294     if(freeMemory)
295       d->tag.set(ID3v1Index, 0);
296   }
297
298   if((tags & APE) && d->hasAPE) {
299     removeBlock(d->APELocation, d->APEOriginalSize);
300     d->APELocation = -1;
301     d->APEFooterLocation = -1;
302     d->hasAPE = false;
303     if(d->hasID3v1) {
304       if(d->ID3v1Location > d->APELocation)
305         d->ID3v1Location -= d->APEOriginalSize;
306     }
307
308     if(freeMemory)
309       d->tag.set(APEIndex, 0);
310   }
311
312   return true;
313 }
314
315 void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
316 {
317   d->ID3v2FrameFactory = factory;
318 }
319
320 long MPEG::File::nextFrameOffset(long position)
321 {
322   bool foundLastSyncPattern = false;
323
324   ByteVector buffer;
325
326   while(true) {
327     seek(position);
328     buffer = readBlock(bufferSize());
329
330     if(buffer.size() <= 0)
331       return -1;
332
333     if(foundLastSyncPattern && secondSynchByte(buffer[0]))
334       return position - 1;
335
336     for(uint i = 0; i < buffer.size() - 1; i++) {
337       if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
338         return position + i;
339     }
340
341     foundLastSyncPattern = uchar(buffer[buffer.size() - 1]) == 0xff;
342     position += buffer.size();
343   }
344 }
345
346 long MPEG::File::previousFrameOffset(long position)
347 {
348   bool foundFirstSyncPattern = false;
349   ByteVector buffer;
350
351   while (position > 0) {
352     long size = ulong(position) < bufferSize() ? position : bufferSize();
353     position -= size;
354
355     seek(position);
356     buffer = readBlock(size);
357
358     if(buffer.size() <= 0)
359       break;
360
361     if(foundFirstSyncPattern && uchar(buffer[buffer.size() - 1]) == 0xff)
362       return position + buffer.size() - 1;
363
364     for(int i = buffer.size() - 2; i >= 0; i--) {
365       if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
366         return position + i;
367     }
368
369     foundFirstSyncPattern = secondSynchByte(buffer[0]);
370   }
371   return -1;
372 }
373
374 long MPEG::File::firstFrameOffset()
375 {
376   long position = 0;
377
378   if(ID3v2Tag())
379     position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize();
380
381   return nextFrameOffset(position);
382 }
383
384 long MPEG::File::lastFrameOffset()
385 {
386   return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length());
387 }
388
389 ////////////////////////////////////////////////////////////////////////////////
390 // private members
391 ////////////////////////////////////////////////////////////////////////////////
392
393 void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
394 {
395   // Look for an ID3v2 tag
396
397   d->ID3v2Location = findID3v2();
398
399   if(d->ID3v2Location >= 0) {
400
401     d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
402
403     d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
404
405     if(ID3v2Tag()->header()->tagSize() <= 0)
406       d->tag.set(ID3v2Index, 0);
407     else
408       d->hasID3v2 = true;
409   }
410
411   // Look for an ID3v1 tag
412
413   d->ID3v1Location = findID3v1();
414
415   if(d->ID3v1Location >= 0) {
416     d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
417     d->hasID3v1 = true;
418   }
419
420   // Look for an APE tag
421
422   findAPE();
423
424   if(d->APELocation >= 0) {
425
426     d->tag.set(APEIndex, new APE::Tag(this, d->APEFooterLocation));
427     d->APEOriginalSize = APETag()->footer()->completeTagSize();
428     d->hasAPE = true;
429   }
430
431   if(readProperties)
432     d->properties = new Properties(this, propertiesStyle);
433
434   // Make sure that we have our default tag types available.
435
436   ID3v2Tag(true);
437   ID3v1Tag(true);
438 }
439
440 long MPEG::File::findID3v2()
441 {
442   // This method is based on the contents of TagLib::File::find(), but because
443   // of some subtlteies -- specifically the need to look for the bit pattern of
444   // an MPEG sync, it has been modified for use here.
445
446   if(isValid() && ID3v2::Header::fileIdentifier().size() <= bufferSize()) {
447
448     // The position in the file that the current buffer starts at.
449
450     long bufferOffset = 0;
451     ByteVector buffer;
452
453     // These variables are used to keep track of a partial match that happens at
454     // the end of a buffer.
455
456     int previousPartialMatch = -1;
457     bool previousPartialSynchMatch = false;
458
459     // Save the location of the current read pointer.  We will restore the
460     // position using seek() before all returns.
461
462     long originalPosition = tell();
463
464     // Start the search at the beginning of the file.
465
466     seek(0);
467
468     // This loop is the crux of the find method.  There are three cases that we
469     // want to account for:
470     // (1) The previously searched buffer contained a partial match of the search
471     // pattern and we want to see if the next one starts with the remainder of
472     // that pattern.
473     //
474     // (2) The search pattern is wholly contained within the current buffer.
475     //
476     // (3) The current buffer ends with a partial match of the pattern.  We will
477     // note this for use in the next itteration, where we will check for the rest
478     // of the pattern.
479
480     for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {
481
482       // (1) previous partial match
483
484       if(previousPartialSynchMatch && secondSynchByte(buffer[0]))
485         return -1;
486
487       if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) {
488         const int patternOffset = (bufferSize() - previousPartialMatch);
489         if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) {
490           seek(originalPosition);
491           return bufferOffset - bufferSize() + previousPartialMatch;
492         }
493       }
494
495       // (2) pattern contained in current buffer
496
497       long location = buffer.find(ID3v2::Header::fileIdentifier());
498       if(location >= 0) {
499         seek(originalPosition);
500         return bufferOffset + location;
501       }
502
503       int firstSynchByte = buffer.find(char(uchar(255)));
504
505       // Here we have to loop because there could be several of the first
506       // (11111111) byte, and we want to check all such instances until we find
507       // a full match (11111111 111) or hit the end of the buffer.
508
509       while(firstSynchByte >= 0) {
510
511         // if this *is not* at the end of the buffer
512
513         if(firstSynchByte < int(buffer.size()) - 1) {
514           if(secondSynchByte(buffer[firstSynchByte + 1])) {
515             // We've found the frame synch pattern.
516             seek(originalPosition);
517             return -1;
518           }
519           else {
520
521             // We found 11111111 at the end of the current buffer indicating a
522             // partial match of the synch pattern.  The find() below should
523             // return -1 and break out of the loop.
524
525             previousPartialSynchMatch = true;
526           }
527         }
528
529         // Check in the rest of the buffer.
530
531         firstSynchByte = buffer.find(char(uchar(255)), firstSynchByte + 1);
532       }
533
534       // (3) partial match
535
536       previousPartialMatch = buffer.endsWithPartialMatch(ID3v2::Header::fileIdentifier());
537
538       bufferOffset += bufferSize();
539     }
540
541     // Since we hit the end of the file, reset the status before continuing.
542
543     clear();
544
545     seek(originalPosition);
546   }
547
548   return -1;
549 }
550
551 long MPEG::File::findID3v1()
552 {
553   if(isValid()) {
554     seek(-128, End);
555     long p = tell();
556
557     if(readBlock(3) == ID3v1::Tag::fileIdentifier())
558       return p;
559   }
560   return -1;
561 }
562
563 void MPEG::File::findAPE()
564 {
565   if(isValid()) {
566     seek(d->hasID3v1 ? -160 : -32, End);
567
568     long p = tell();
569
570     if(readBlock(8) == APE::Tag::fileIdentifier()) {
571       d->APEFooterLocation = p;
572       seek(d->APEFooterLocation);
573       APE::Footer footer(readBlock(APE::Footer::size()));
574       d->APELocation = d->APEFooterLocation - footer.completeTagSize()
575         + APE::Footer::size();
576       return;
577     }
578   }
579
580   d->APELocation = -1;
581   d->APEFooterLocation = -1;
582 }
583
584 bool MPEG::File::secondSynchByte(char byte)
585 {
586   if(uchar(byte) == 0xff)
587     return false;
588
589   std::bitset<8> b(byte);
590
591   // check to see if the byte matches 111xxxxx
592   return b.test(7) && b.test(6) && b.test(5);
593 }