Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / mpc / mpcfile.cpp
1 /***************************************************************************
2     copyright            : (C) 2004 by Allan Sandfeld Jensen
3     email                : kde@carewolf.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 <tbytevector.h>
27 #include <tstring.h>
28 #include <tagunion.h>
29 #include <tdebug.h>
30
31 #include "mpcfile.h"
32 #include "id3v1tag.h"
33 #include "id3v2header.h"
34 #include "apetag.h"
35 #include "apefooter.h"
36
37 using namespace TagLib;
38
39 namespace
40 {
41   enum { APEIndex, ID3v1Index };
42 }
43
44 class MPC::File::FilePrivate
45 {
46 public:
47   FilePrivate() :
48     APELocation(-1),
49     APESize(0),
50     ID3v1Location(-1),
51     ID3v2Header(0),
52     ID3v2Location(-1),
53     ID3v2Size(0),
54     properties(0),
55     scanned(false),
56     hasAPE(false),
57     hasID3v1(false),
58     hasID3v2(false) {}
59
60   ~FilePrivate()
61   {
62     delete ID3v2Header;
63     delete properties;
64   }
65
66   long APELocation;
67   uint APESize;
68
69   long ID3v1Location;
70
71   ID3v2::Header *ID3v2Header;
72   long ID3v2Location;
73   uint ID3v2Size;
74
75   TagUnion tag;
76
77   Properties *properties;
78   bool scanned;
79
80   // These indicate whether the file *on disk* has these tags, not if
81   // this data structure does.  This is used in computing offsets.
82
83   bool hasAPE;
84   bool hasID3v1;
85   bool hasID3v2;
86 };
87
88 ////////////////////////////////////////////////////////////////////////////////
89 // public members
90 ////////////////////////////////////////////////////////////////////////////////
91
92 MPC::File::File(FileName file, bool readProperties,
93                 Properties::ReadStyle propertiesStyle) : TagLib::File(file)
94 {
95   d = new FilePrivate;
96   read(readProperties, propertiesStyle);
97 }
98
99 MPC::File::~File()
100 {
101   delete d;
102 }
103
104 TagLib::Tag *MPC::File::tag() const
105 {
106   return &d->tag;
107 }
108
109 MPC::Properties *MPC::File::audioProperties() const
110 {
111   return d->properties;
112 }
113
114 bool MPC::File::save()
115 {
116   if(readOnly()) {
117     debug("MPC::File::save() -- File is read only.");
118     return false;
119   }
120
121   // Possibly strip ID3v2 tag
122
123   if(d->hasID3v2 && !d->ID3v2Header) {
124     removeBlock(d->ID3v2Location, d->ID3v2Size);
125     d->hasID3v2 = false;
126     if(d->hasID3v1)
127       d->ID3v1Location -= d->ID3v2Size;
128     if(d->hasAPE)
129       d->APELocation -= d->ID3v2Size;
130   }
131
132   // Update ID3v1 tag
133
134   if(ID3v1Tag()) {
135     if(d->hasID3v1) {
136       seek(d->ID3v1Location);
137       writeBlock(ID3v1Tag()->render());
138     }
139     else {
140       seek(0, End);
141       d->ID3v1Location = tell();
142       writeBlock(ID3v1Tag()->render());
143       d->hasID3v1 = true;
144     }
145   } else
146     if(d->hasID3v1) {
147       removeBlock(d->ID3v1Location, 128);
148       d->hasID3v1 = false;
149       if(d->hasAPE) {
150         if(d->APELocation > d->ID3v1Location)
151           d->APELocation -= 128;
152       }
153     }
154
155   // Update APE tag
156
157   if(APETag()) {
158     if(d->hasAPE)
159       insert(APETag()->render(), d->APELocation, d->APESize);
160     else {
161       if(d->hasID3v1)  {
162         insert(APETag()->render(), d->ID3v1Location, 0);
163         d->APESize = APETag()->footer()->completeTagSize();
164         d->hasAPE = true;
165         d->APELocation = d->ID3v1Location;
166         d->ID3v1Location += d->APESize;
167       }
168       else {
169         seek(0, End);
170         d->APELocation = tell();
171         writeBlock(APETag()->render());
172         d->APESize = APETag()->footer()->completeTagSize();
173         d->hasAPE = true;
174       }
175     }
176   }
177   else
178     if(d->hasAPE) {
179       removeBlock(d->APELocation, d->APESize);
180       d->hasAPE = false;
181       if(d->hasID3v1) {
182         if(d->ID3v1Location > d->APELocation)
183           d->ID3v1Location -= d->APESize;
184       }
185     }
186
187   return true;
188 }
189
190 ID3v1::Tag *MPC::File::ID3v1Tag(bool create)
191 {
192   return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
193 }
194
195 APE::Tag *MPC::File::APETag(bool create)
196 {
197   return d->tag.access<APE::Tag>(APEIndex, create);
198 }
199
200 void MPC::File::strip(int tags)
201 {
202   if(tags & ID3v1) {
203     d->tag.set(ID3v1Index, 0);
204     APETag(true);
205   }
206
207   if(tags & ID3v2) {
208     delete d->ID3v2Header;
209     d->ID3v2Header = 0;
210   }
211
212   if(tags & APE) {
213     d->tag.set(APEIndex, 0);
214
215     if(!ID3v1Tag())
216       APETag(true);
217   }
218 }
219
220 void MPC::File::remove(int tags)
221 {
222   strip(tags);
223 }
224
225
226 ////////////////////////////////////////////////////////////////////////////////
227 // private members
228 ////////////////////////////////////////////////////////////////////////////////
229
230 void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
231 {
232   // Look for an ID3v1 tag
233
234   d->ID3v1Location = findID3v1();
235
236   if(d->ID3v1Location >= 0) {
237     d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
238     d->hasID3v1 = true;
239   }
240
241   // Look for an APE tag
242
243   findAPE();
244
245   d->APELocation = findAPE();
246
247   if(d->APELocation >= 0) {
248     d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
249
250     d->APESize = APETag()->footer()->completeTagSize();
251     d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
252     d->hasAPE = true;
253   }
254
255   if(!d->hasID3v1)
256     APETag(true);
257
258   // Look for and skip an ID3v2 tag
259
260   d->ID3v2Location = findID3v2();
261
262   if(d->ID3v2Location >= 0) {
263     seek(d->ID3v2Location);
264     d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
265     d->ID3v2Size = d->ID3v2Header->completeTagSize();
266     d->hasID3v2 = true;
267   }
268
269   if(d->hasID3v2)
270     seek(d->ID3v2Location + d->ID3v2Size);
271   else
272     seek(0);
273
274   // Look for MPC metadata
275
276   if(readProperties) {
277     d->properties = new Properties(readBlock(MPC::HeaderSize),
278                                    length() - d->ID3v2Size - d->APESize);
279   }
280 }
281
282 long MPC::File::findAPE()
283 {
284   if(!isValid())
285     return -1;
286
287   if(d->hasID3v1)
288     seek(-160, End);
289   else
290     seek(-32, End);
291
292   long p = tell();
293
294   if(readBlock(8) == APE::Tag::fileIdentifier())
295     return p;
296
297   return -1;
298 }
299
300 long MPC::File::findID3v1()
301 {
302   if(!isValid())
303     return -1;
304
305   seek(-128, End);
306   long p = tell();
307
308   if(readBlock(3) == ID3v1::Tag::fileIdentifier())
309     return p;
310
311   return -1;
312 }
313
314 long MPC::File::findID3v2()
315 {
316   if(!isValid())
317     return -1;
318
319   seek(0);
320
321   if(readBlock(3) == ID3v2::Header::fileIdentifier())
322     return 0;
323
324   return -1;
325 }