Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / trueaudio / trueaudiofile.cpp
1 /***************************************************************************
2     copyright            : (C) 2006 by Lukáš Lalinský
3     email                : lalinsky@gmail.com
4
5     copyright            : (C) 2004 by Allan Sandfeld Jensen
6     email                : kde@carewolf.org
7                            (original MPC implementation)
8  ***************************************************************************/
9
10 /***************************************************************************
11  *   This library is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU Lesser General Public License version   *
13  *   2.1 as published by the Free Software Foundation.                     *
14  *                                                                         *
15  *   This library is distributed in the hope that it will be useful, but   *
16  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
18  *   Lesser General Public License for more details.                       *
19  *                                                                         *
20  *   You should have received a copy of the GNU Lesser General Public      *
21  *   License along with this library; if not, write to the Free Software   *
22  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
23  *   USA                                                                   *
24  *                                                                         *
25  *   Alternatively, this file is available under the Mozilla Public        *
26  *   License Version 1.1.  You may obtain a copy of the License at         *
27  *   http://www.mozilla.org/MPL/                                           *
28  ***************************************************************************/
29
30 #include <tbytevector.h>
31 #include <tstring.h>
32 #include <tdebug.h>
33 #include <tagunion.h>
34
35 #include "trueaudiofile.h"
36 #include "id3v1tag.h"
37 #include "id3v2tag.h"
38 #include "id3v2header.h"
39
40 using namespace TagLib;
41
42 namespace
43 {
44   enum { ID3v2Index = 0, ID3v1Index = 1 };
45 }
46
47 class TrueAudio::File::FilePrivate
48 {
49 public:
50   FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
51     ID3v2FrameFactory(frameFactory),
52     ID3v2Location(-1),
53     ID3v2OriginalSize(0),
54     ID3v1Location(-1),
55     properties(0),
56     scanned(false),
57     hasID3v1(false),
58     hasID3v2(false) {}
59
60   ~FilePrivate()
61   {
62     delete properties;
63   }
64
65   const ID3v2::FrameFactory *ID3v2FrameFactory;
66   long ID3v2Location;
67   uint ID3v2OriginalSize;
68
69   long ID3v1Location;
70
71   TagUnion tag;
72
73   Properties *properties;
74   bool scanned;
75
76   // These indicate whether the file *on disk* has these tags, not if
77   // this data structure does.  This is used in computing offsets.
78
79   bool hasID3v1;
80   bool hasID3v2;
81 };
82
83 ////////////////////////////////////////////////////////////////////////////////
84 // public members
85 ////////////////////////////////////////////////////////////////////////////////
86
87 TrueAudio::File::File(FileName file, bool readProperties,
88                  Properties::ReadStyle propertiesStyle) : TagLib::File(file)
89 {
90   d = new FilePrivate;
91   if(isOpen())
92     read(readProperties, propertiesStyle);
93 }
94
95 TrueAudio::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
96                  bool readProperties, Properties::ReadStyle propertiesStyle) :
97   TagLib::File(file)
98 {
99   d = new FilePrivate(frameFactory);
100   if(isOpen())
101     read(readProperties, propertiesStyle);
102 }
103
104 TrueAudio::File::~File()
105 {
106   delete d;
107 }
108
109 TagLib::Tag *TrueAudio::File::tag() const
110 {
111   return &d->tag;
112 }
113
114 TrueAudio::Properties *TrueAudio::File::audioProperties() const
115 {
116   return d->properties;
117 }
118
119 void TrueAudio::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
120 {
121   d->ID3v2FrameFactory = factory;
122 }
123
124 bool TrueAudio::File::save()
125 {
126   if(readOnly()) {
127     debug("TrueAudio::File::save() -- File is read only.");
128     return false;
129   }
130
131   // Update ID3v2 tag
132
133   if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
134     if(!d->hasID3v2) {
135       d->ID3v2Location = 0;
136       d->ID3v2OriginalSize = 0;
137     }
138     ByteVector data = ID3v2Tag()->render();
139     insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
140     d->ID3v1Location -= d->ID3v2OriginalSize - data.size();
141     d->ID3v2OriginalSize = data.size();
142     d->hasID3v2 = true;
143   }
144   else if(d->hasID3v2) {
145     removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
146     d->ID3v1Location -= d->ID3v2OriginalSize;
147     d->ID3v2Location = -1;
148     d->ID3v2OriginalSize = 0;
149     d->hasID3v2 = false;
150   }
151
152   // Update ID3v1 tag
153
154   if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
155     if(!d->hasID3v1) {
156       seek(0, End);
157       d->ID3v1Location = tell();
158     }
159     else
160       seek(d->ID3v1Location);
161     writeBlock(ID3v1Tag()->render());
162     d->hasID3v1 = true;
163   }
164   else if(d->hasID3v1) {
165     removeBlock(d->ID3v1Location, 128);
166     d->ID3v1Location = -1;
167     d->hasID3v1 = false;
168   }
169
170   return true;
171 }
172
173 ID3v1::Tag *TrueAudio::File::ID3v1Tag(bool create)
174 {
175   return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
176 }
177
178 ID3v2::Tag *TrueAudio::File::ID3v2Tag(bool create)
179 {
180   return d->tag.access<ID3v2::Tag>(ID3v2Index, create);
181 }
182
183 void TrueAudio::File::strip(int tags)
184 {
185   if(tags & ID3v1) {
186     d->tag.set(ID3v1Index, 0);
187     ID3v2Tag(true);
188   }
189
190   if(tags & ID3v2) {
191     d->tag.set(ID3v2Index, 0);
192
193     if(!ID3v1Tag())
194       ID3v2Tag(true);
195   }
196 }
197
198
199 ////////////////////////////////////////////////////////////////////////////////
200 // private members
201 ////////////////////////////////////////////////////////////////////////////////
202
203 void TrueAudio::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
204 {
205   // Look for an ID3v2 tag
206
207   d->ID3v2Location = findID3v2();
208
209   if(d->ID3v2Location >= 0) {
210
211     d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
212
213     d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
214
215     if(ID3v2Tag()->header()->tagSize() <= 0)
216       d->tag.set(ID3v2Index, 0);
217     else
218       d->hasID3v2 = true;
219   }
220
221   // Look for an ID3v1 tag
222
223   d->ID3v1Location = findID3v1();
224
225   if(d->ID3v1Location >= 0) {
226     d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
227     d->hasID3v1 = true;
228   }
229
230   if(!d->hasID3v1)
231     ID3v2Tag(true);
232
233   // Look for TrueAudio metadata
234
235   if(readProperties) {
236     if(d->ID3v2Location >= 0) {
237       seek(d->ID3v2Location + d->ID3v2OriginalSize);
238       d->properties = new Properties(readBlock(TrueAudio::HeaderSize),
239                                      length() - d->ID3v2OriginalSize);
240     }
241     else {
242       seek(0);
243       d->properties = new Properties(readBlock(TrueAudio::HeaderSize),
244                                      length());
245     }
246   }
247 }
248
249 long TrueAudio::File::findID3v1()
250 {
251   if(!isValid())
252     return -1;
253
254   seek(-128, End);
255   long p = tell();
256
257   if(readBlock(3) == ID3v1::Tag::fileIdentifier())
258     return p;
259
260   return -1;
261 }
262
263 long TrueAudio::File::findID3v2()
264 {
265   if(!isValid())
266     return -1;
267
268   seek(0);
269
270   if(readBlock(3) == ID3v2::Header::fileIdentifier())
271     return 0;
272
273   return -1;
274 }