1 /***************************************************************************
2 copyright : (C) 2004 by Allan Sandfeld Jensen
3 email : kde@carewolf.com
4 ***************************************************************************/
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. *
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. *
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 *
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 ***************************************************************************/
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)
39 #include "apefooter.h"
42 using namespace TagLib;
45 class APE::Tag::TagPrivate
48 TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
56 ItemListMap itemListMap;
59 ////////////////////////////////////////////////////////////////////////////////
61 ////////////////////////////////////////////////////////////////////////////////
63 APE::Tag::Tag() : TagLib::Tag()
68 APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag()
72 d->footerLocation = footerLocation;
82 ByteVector APE::Tag::fileIdentifier()
84 return ByteVector::fromCString("APETAGEX");
87 String APE::Tag::title() const
89 if(d->itemListMap["TITLE"].isEmpty())
91 return d->itemListMap["TITLE"].toString();
94 String APE::Tag::artist() const
96 if(d->itemListMap["ARTIST"].isEmpty())
98 return d->itemListMap["ARTIST"].toString();
101 String APE::Tag::album() const
103 if(d->itemListMap["ALBUM"].isEmpty())
105 return d->itemListMap["ALBUM"].toString();
108 String APE::Tag::comment() const
110 if(d->itemListMap["COMMENT"].isEmpty())
112 return d->itemListMap["COMMENT"].toString();
115 String APE::Tag::genre() const
117 if(d->itemListMap["GENRE"].isEmpty())
119 return d->itemListMap["GENRE"].toString();
122 TagLib::uint APE::Tag::year() const
124 if(d->itemListMap["YEAR"].isEmpty())
126 return d->itemListMap["YEAR"].toString().toInt();
129 TagLib::uint APE::Tag::track() const
131 if(d->itemListMap["TRACK"].isEmpty())
133 return d->itemListMap["TRACK"].toString().toInt();
136 void APE::Tag::setTitle(const String &s)
138 addValue("TITLE", s, true);
141 void APE::Tag::setArtist(const String &s)
143 addValue("ARTIST", s, true);
146 void APE::Tag::setAlbum(const String &s)
148 addValue("ALBUM", s, true);
151 void APE::Tag::setComment(const String &s)
153 addValue("COMMENT", s, true);
156 void APE::Tag::setGenre(const String &s)
158 addValue("GENRE", s, true);
161 void APE::Tag::setYear(uint i)
166 addValue("YEAR", String::number(i), true);
169 void APE::Tag::setTrack(uint i)
174 addValue("TRACK", String::number(i), true);
177 APE::Footer *APE::Tag::footer() const
182 const APE::ItemListMap& APE::Tag::itemListMap() const
184 return d->itemListMap;
187 void APE::Tag::removeItem(const String &key)
189 Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
190 if(it != d->itemListMap.end())
191 d->itemListMap.erase(it);
194 void APE::Tag::addValue(const String &key, const String &value, bool replace)
198 if(!value.isEmpty()) {
199 if(d->itemListMap.contains(key) || !replace)
200 d->itemListMap[key.upper()].appendValue(value);
202 setItem(key, Item(key, value));
206 void APE::Tag::setItem(const String &key, const Item &item)
208 d->itemListMap.insert(key.upper(), item);
211 ////////////////////////////////////////////////////////////////////////////////
213 ////////////////////////////////////////////////////////////////////////////////
215 void APE::Tag::read()
217 if(d->file && d->file->isValid()) {
219 d->file->seek(d->footerLocation);
220 d->footer.setData(d->file->readBlock(Footer::size()));
222 if(d->footer.tagSize() <= Footer::size() ||
223 d->footer.tagSize() > uint(d->file->length()))
226 d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
227 parse(d->file->readBlock(d->footer.tagSize() - Footer::size()));
231 ByteVector APE::Tag::render() const
237 for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
238 it != d->itemListMap.end(); ++it)
240 data.append(it->second.render());
245 d->footer.setItemCount(itemCount);
246 d->footer.setTagSize(data.size() + Footer::size());
247 d->footer.setHeaderPresent(true);
249 return d->footer.renderHeader() + data + d->footer.renderFooter();
252 void APE::Tag::parse(const ByteVector &data)
256 // 11 bytes is the minimum size for an APE item
258 for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
260 item.parse(data.mid(pos));
262 d->itemListMap.insert(item.key().upper(), item);