Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / ape / apetag.cpp
1 /***************************************************************************
2     copyright            : (C) 2004 by Allan Sandfeld Jensen
3     email                : kde@carewolf.com
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 #ifdef __SUNPRO_CC
27 // Sun Studio finds multiple specializations of Map because
28 // it considers specializations with and without class types
29 // to be different; this define forces Map to use only the
30 // specialization with the class keyword.
31 #define WANT_CLASS_INSTANTIATION_OF_MAP (1)
32 #endif
33
34 #include <tfile.h>
35 #include <tstring.h>
36 #include <tmap.h>
37
38 #include "apetag.h"
39 #include "apefooter.h"
40 #include "apeitem.h"
41
42 using namespace TagLib;
43 using namespace APE;
44
45 class APE::Tag::TagPrivate
46 {
47 public:
48   TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
49
50   File *file;
51   long footerLocation;
52   long tagLength;
53
54   Footer footer;
55
56   ItemListMap itemListMap;
57 };
58
59 ////////////////////////////////////////////////////////////////////////////////
60 // public methods
61 ////////////////////////////////////////////////////////////////////////////////
62
63 APE::Tag::Tag() : TagLib::Tag()
64 {
65   d = new TagPrivate;
66 }
67
68 APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag()
69 {
70   d = new TagPrivate;
71   d->file = file;
72   d->footerLocation = footerLocation;
73
74   read();
75 }
76
77 APE::Tag::~Tag()
78 {
79   delete d;
80 }
81
82 ByteVector APE::Tag::fileIdentifier()
83 {
84   return ByteVector::fromCString("APETAGEX");
85 }
86
87 String APE::Tag::title() const
88 {
89   if(d->itemListMap["TITLE"].isEmpty())
90     return String::null;
91   return d->itemListMap["TITLE"].toString();
92 }
93
94 String APE::Tag::artist() const
95 {
96   if(d->itemListMap["ARTIST"].isEmpty())
97     return String::null;
98   return d->itemListMap["ARTIST"].toString();
99 }
100
101 String APE::Tag::album() const
102 {
103   if(d->itemListMap["ALBUM"].isEmpty())
104     return String::null;
105   return d->itemListMap["ALBUM"].toString();
106 }
107
108 String APE::Tag::comment() const
109 {
110   if(d->itemListMap["COMMENT"].isEmpty())
111     return String::null;
112   return d->itemListMap["COMMENT"].toString();
113 }
114
115 String APE::Tag::genre() const
116 {
117   if(d->itemListMap["GENRE"].isEmpty())
118     return String::null;
119   return d->itemListMap["GENRE"].toString();
120 }
121
122 TagLib::uint APE::Tag::year() const
123 {
124   if(d->itemListMap["YEAR"].isEmpty())
125     return 0;
126   return d->itemListMap["YEAR"].toString().toInt();
127 }
128
129 TagLib::uint APE::Tag::track() const
130 {
131   if(d->itemListMap["TRACK"].isEmpty())
132     return 0;
133   return d->itemListMap["TRACK"].toString().toInt();
134 }
135
136 void APE::Tag::setTitle(const String &s)
137 {
138   addValue("TITLE", s, true);
139 }
140
141 void APE::Tag::setArtist(const String &s)
142 {
143   addValue("ARTIST", s, true);
144 }
145
146 void APE::Tag::setAlbum(const String &s)
147 {
148   addValue("ALBUM", s, true);
149 }
150
151 void APE::Tag::setComment(const String &s)
152 {
153   addValue("COMMENT", s, true);
154 }
155
156 void APE::Tag::setGenre(const String &s)
157 {
158   addValue("GENRE", s, true);
159 }
160
161 void APE::Tag::setYear(uint i)
162 {
163   if(i <= 0)
164     removeItem("YEAR");
165   else
166     addValue("YEAR", String::number(i), true);
167 }
168
169 void APE::Tag::setTrack(uint i)
170 {
171   if(i <= 0)
172     removeItem("TRACK");
173   else
174     addValue("TRACK", String::number(i), true);
175 }
176
177 APE::Footer *APE::Tag::footer() const
178 {
179   return &d->footer;
180 }
181
182 const APE::ItemListMap& APE::Tag::itemListMap() const
183 {
184   return d->itemListMap;
185 }
186
187 void APE::Tag::removeItem(const String &key)
188 {
189   Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
190   if(it != d->itemListMap.end())
191     d->itemListMap.erase(it);
192 }
193
194 void APE::Tag::addValue(const String &key, const String &value, bool replace)
195 {
196   if(replace)
197     removeItem(key);
198   if(!value.isEmpty()) {
199     if(d->itemListMap.contains(key) || !replace)
200       d->itemListMap[key.upper()].appendValue(value);
201     else
202       setItem(key, Item(key, value));
203   }
204 }
205
206 void APE::Tag::setItem(const String &key, const Item &item)
207 {
208   d->itemListMap.insert(key.upper(), item);
209 }
210
211 ////////////////////////////////////////////////////////////////////////////////
212 // protected methods
213 ////////////////////////////////////////////////////////////////////////////////
214
215 void APE::Tag::read()
216 {
217   if(d->file && d->file->isValid()) {
218
219     d->file->seek(d->footerLocation);
220     d->footer.setData(d->file->readBlock(Footer::size()));
221
222     if(d->footer.tagSize() <= Footer::size() ||
223        d->footer.tagSize() > uint(d->file->length()))
224       return;
225
226     d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
227     parse(d->file->readBlock(d->footer.tagSize() - Footer::size()));
228   }
229 }
230
231 ByteVector APE::Tag::render() const
232 {
233   ByteVector data;
234   uint itemCount = 0;
235
236   {
237     for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
238         it != d->itemListMap.end(); ++it)
239     {
240       data.append(it->second.render());
241       itemCount++;
242     }
243   }
244
245   d->footer.setItemCount(itemCount);
246   d->footer.setTagSize(data.size() + Footer::size());
247   d->footer.setHeaderPresent(true);
248
249   return d->footer.renderHeader() + data + d->footer.renderFooter();
250 }
251
252 void APE::Tag::parse(const ByteVector &data)
253 {
254   uint pos = 0;
255
256   // 11 bytes is the minimum size for an APE item
257
258   for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
259     APE::Item item;
260     item.parse(data.mid(pos));
261
262     d->itemListMap.insert(item.key().upper(), item);
263
264     pos += item.size();
265   }
266 }