Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / wavpack / wavpackfile.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 "wavpackfile.h"
36 #include "id3v1tag.h"
37 #include "id3v2header.h"
38 #include "apetag.h"
39 #include "apefooter.h"
40
41 using namespace TagLib;
42
43 namespace
44 {
45   enum { APEIndex, ID3v1Index };
46 }
47
48 class WavPack::File::FilePrivate
49 {
50 public:
51   FilePrivate() :
52     APELocation(-1),
53     APESize(0),
54     ID3v1Location(-1),
55     properties(0),
56     scanned(false),
57     hasAPE(false),
58     hasID3v1(false) {}
59
60   ~FilePrivate()
61   {
62     delete properties;
63   }
64
65   long APELocation;
66   uint APESize;
67
68   long ID3v1Location;
69
70   TagUnion tag;
71
72   Properties *properties;
73   bool scanned;
74
75   // These indicate whether the file *on disk* has these tags, not if
76   // this data structure does.  This is used in computing offsets.
77
78   bool hasAPE;
79   bool hasID3v1;
80 };
81
82 ////////////////////////////////////////////////////////////////////////////////
83 // public members
84 ////////////////////////////////////////////////////////////////////////////////
85
86 WavPack::File::File(FileName file, bool readProperties,
87                 Properties::ReadStyle propertiesStyle) : TagLib::File(file)
88 {
89   d = new FilePrivate;
90   read(readProperties, propertiesStyle);
91 }
92
93 WavPack::File::~File()
94 {
95   delete d;
96 }
97
98 TagLib::Tag *WavPack::File::tag() const
99 {
100   return &d->tag;
101 }
102
103 WavPack::Properties *WavPack::File::audioProperties() const
104 {
105   return d->properties;
106 }
107
108 bool WavPack::File::save()
109 {
110   if(readOnly()) {
111     debug("WavPack::File::save() -- File is read only.");
112     return false;
113   }
114
115   // Update ID3v1 tag
116
117   if(ID3v1Tag()) {
118     if(d->hasID3v1) {
119       seek(d->ID3v1Location);
120       writeBlock(ID3v1Tag()->render());
121     }
122     else {
123       seek(0, End);
124       d->ID3v1Location = tell();
125       writeBlock(ID3v1Tag()->render());
126       d->hasID3v1 = true;
127     }
128   }
129   else {
130     if(d->hasID3v1) {
131       removeBlock(d->ID3v1Location, 128);
132       d->hasID3v1 = false;
133       if(d->hasAPE) {
134         if(d->APELocation > d->ID3v1Location)
135           d->APELocation -= 128;
136       }
137     }
138   }
139
140   // Update APE tag
141
142   if(APETag()) {
143     if(d->hasAPE)
144       insert(APETag()->render(), d->APELocation, d->APESize);
145     else {
146       if(d->hasID3v1)  {
147         insert(APETag()->render(), d->ID3v1Location, 0);
148         d->APESize = APETag()->footer()->completeTagSize();
149         d->hasAPE = true;
150         d->APELocation = d->ID3v1Location;
151         d->ID3v1Location += d->APESize;
152       }
153       else {
154         seek(0, End);
155         d->APELocation = tell();
156         writeBlock(APETag()->render());
157         d->APESize = APETag()->footer()->completeTagSize();
158         d->hasAPE = true;
159       }
160     }
161   }
162   else {
163     if(d->hasAPE) {
164       removeBlock(d->APELocation, d->APESize);
165       d->hasAPE = false;
166       if(d->hasID3v1) {
167         if(d->ID3v1Location > d->APELocation) {
168           d->ID3v1Location -= d->APESize;
169         }
170       }
171     }
172   }
173
174    return true;
175 }
176
177 ID3v1::Tag *WavPack::File::ID3v1Tag(bool create)
178 {
179   return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
180 }
181
182 APE::Tag *WavPack::File::APETag(bool create)
183 {
184   return d->tag.access<APE::Tag>(APEIndex, create);
185 }
186
187 void WavPack::File::strip(int tags)
188 {
189   if(tags & ID3v1) {
190     d->tag.set(ID3v1Index, 0);
191     APETag(true);
192   }
193
194   if(tags & APE) {
195     d->tag.set(APEIndex, 0);
196
197     if(!ID3v1Tag())
198       APETag(true);
199   }
200 }
201
202 ////////////////////////////////////////////////////////////////////////////////
203 // private members
204 ////////////////////////////////////////////////////////////////////////////////
205
206 void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
207 {
208   // Look for an ID3v1 tag
209
210   d->ID3v1Location = findID3v1();
211
212   if(d->ID3v1Location >= 0) {
213     d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
214     d->hasID3v1 = true;
215   }
216
217   // Look for an APE tag
218
219   d->APELocation = findAPE();
220
221   if(d->APELocation >= 0) {
222     d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
223     d->APESize = APETag()->footer()->completeTagSize();
224     d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
225     d->hasAPE = true;
226   }
227
228   if(!d->hasID3v1)
229     APETag(true);
230
231   // Look for WavPack audio properties
232
233   if(readProperties) {
234     seek(0);
235     d->properties = new Properties(readBlock(WavPack::HeaderSize),
236                                    length() - d->APESize);
237   }
238 }
239
240 long WavPack::File::findAPE()
241 {
242   if(!isValid())
243     return -1;
244
245   if(d->hasID3v1)
246     seek(-160, End);
247   else
248     seek(-32, End);
249
250   long p = tell();
251
252   if(readBlock(8) == APE::Tag::fileIdentifier())
253     return p;
254
255   return -1;
256 }
257
258 long WavPack::File::findID3v1()
259 {
260   if(!isValid())
261     return -1;
262
263   seek(-128, End);
264   long p = tell();
265
266   if(readBlock(3) == ID3v1::Tag::fileIdentifier())
267     return p;
268
269   return -1;
270 }