1 /***************************************************************************
2 copyright : (C) 2002 - 2008 by Scott Wheeler
3 email : wheeler@kde.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., 51 Franklin Street, Fifth Floor, Boston, MA *
19 * 02110-1301 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 <tdebug.h>
27 #include <tfile.h>
28
29 #include "id3v1tag.h"
30 #include "id3v1genres.h"
31
32 using namespace TagLib;
33 using namespace ID3v1;
34
35 namespace
36 {
37 const ID3v1::StringHandler defaultStringHandler;
38 const ID3v1::StringHandler *stringHandler = &defaultStringHandler;
39 }
40
41 class ID3v1::Tag::TagPrivate
42 {
43 public:
TagPrivate()44 TagPrivate() :
45 file(0),
46 tagOffset(0),
47 track(0),
48 genre(255) {}
49
50 File *file;
51 long tagOffset;
52
53 String title;
54 String artist;
55 String album;
56 String year;
57 String comment;
58 unsigned char track;
59 unsigned char genre;
60 };
61
62 ////////////////////////////////////////////////////////////////////////////////
63 // StringHandler implementation
64 ////////////////////////////////////////////////////////////////////////////////
65
StringHandler()66 StringHandler::StringHandler()
67 {
68 }
69
parse(const ByteVector & data) const70 String ID3v1::StringHandler::parse(const ByteVector &data) const
71 {
72 return String(data, String::Latin1).stripWhiteSpace();
73 }
74
render(const String & s) const75 ByteVector ID3v1::StringHandler::render(const String &s) const
76 {
77 if(s.isLatin1())
78 return s.data(String::Latin1);
79 else
80 return ByteVector();
81 }
82
83 ////////////////////////////////////////////////////////////////////////////////
84 // public methods
85 ////////////////////////////////////////////////////////////////////////////////
86
Tag()87 ID3v1::Tag::Tag() :
88 TagLib::Tag(),
89 d(new TagPrivate())
90 {
91 }
92
Tag(File * file,long tagOffset)93 ID3v1::Tag::Tag(File *file, long tagOffset) :
94 TagLib::Tag(),
95 d(new TagPrivate())
96 {
97 d->file = file;
98 d->tagOffset = tagOffset;
99
100 read();
101 }
102
~Tag()103 ID3v1::Tag::~Tag()
104 {
105 delete d;
106 }
107
render() const108 ByteVector ID3v1::Tag::render() const
109 {
110 ByteVector data;
111
112 data.append(fileIdentifier());
113 data.append(stringHandler->render(d->title).resize(30));
114 data.append(stringHandler->render(d->artist).resize(30));
115 data.append(stringHandler->render(d->album).resize(30));
116 data.append(stringHandler->render(d->year).resize(4));
117 data.append(stringHandler->render(d->comment).resize(28));
118 data.append(char(0));
119 data.append(char(d->track));
120 data.append(char(d->genre));
121
122 return data;
123 }
124
fileIdentifier()125 ByteVector ID3v1::Tag::fileIdentifier()
126 {
127 return ByteVector::fromCString("TAG");
128 }
129
title() const130 String ID3v1::Tag::title() const
131 {
132 return d->title;
133 }
134
artist() const135 String ID3v1::Tag::artist() const
136 {
137 return d->artist;
138 }
139
album() const140 String ID3v1::Tag::album() const
141 {
142 return d->album;
143 }
144
comment() const145 String ID3v1::Tag::comment() const
146 {
147 return d->comment;
148 }
149
genre() const150 String ID3v1::Tag::genre() const
151 {
152 return ID3v1::genre(d->genre);
153 }
154
year() const155 unsigned int ID3v1::Tag::year() const
156 {
157 return d->year.toInt();
158 }
159
track() const160 unsigned int ID3v1::Tag::track() const
161 {
162 return d->track;
163 }
164
setTitle(const String & s)165 void ID3v1::Tag::setTitle(const String &s)
166 {
167 d->title = s;
168 }
169
setArtist(const String & s)170 void ID3v1::Tag::setArtist(const String &s)
171 {
172 d->artist = s;
173 }
174
setAlbum(const String & s)175 void ID3v1::Tag::setAlbum(const String &s)
176 {
177 d->album = s;
178 }
179
setComment(const String & s)180 void ID3v1::Tag::setComment(const String &s)
181 {
182 d->comment = s;
183 }
184
setGenre(const String & s)185 void ID3v1::Tag::setGenre(const String &s)
186 {
187 d->genre = ID3v1::genreIndex(s);
188 }
189
setYear(unsigned int i)190 void ID3v1::Tag::setYear(unsigned int i)
191 {
192 d->year = i > 0 ? String::number(i) : String();
193 }
194
setTrack(unsigned int i)195 void ID3v1::Tag::setTrack(unsigned int i)
196 {
197 d->track = i < 256 ? i : 0;
198 }
199
genreNumber() const200 unsigned int ID3v1::Tag::genreNumber() const
201 {
202 return d->genre;
203 }
204
setGenreNumber(unsigned int i)205 void ID3v1::Tag::setGenreNumber(unsigned int i)
206 {
207 d->genre = i < 256 ? i : 255;
208 }
209
setStringHandler(const StringHandler * handler)210 void ID3v1::Tag::setStringHandler(const StringHandler *handler)
211 {
212 if(handler)
213 stringHandler = handler;
214 else
215 stringHandler = &defaultStringHandler;
216 }
217
218 ////////////////////////////////////////////////////////////////////////////////
219 // protected methods
220 ////////////////////////////////////////////////////////////////////////////////
221
read()222 void ID3v1::Tag::read()
223 {
224 if(d->file && d->file->isValid()) {
225 d->file->seek(d->tagOffset);
226 // read the tag -- always 128 bytes
227 const ByteVector data = d->file->readBlock(128);
228
229 // some initial sanity checking
230 if(data.size() == 128 && data.startsWith("TAG"))
231 parse(data);
232 else
233 debug("ID3v1 tag is not valid or could not be read at the specified offset.");
234 }
235 }
236
parse(const ByteVector & data)237 void ID3v1::Tag::parse(const ByteVector &data)
238 {
239 int offset = 3;
240
241 d->title = stringHandler->parse(data.mid(offset, 30));
242 offset += 30;
243
244 d->artist = stringHandler->parse(data.mid(offset, 30));
245 offset += 30;
246
247 d->album = stringHandler->parse(data.mid(offset, 30));
248 offset += 30;
249
250 d->year = stringHandler->parse(data.mid(offset, 4));
251 offset += 4;
252
253 // Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this
254 // is not a bug in TagLib. Since a zeroed byte is what we would expect to
255 // indicate the end of a C-String, specifically the comment string, a value of
256 // zero must be assumed to be just that.
257
258 if(data[offset + 28] == 0 && data[offset + 29] != 0) {
259 // ID3v1.1 detected
260
261 d->comment = stringHandler->parse(data.mid(offset, 28));
262 d->track = static_cast<unsigned char>(data[offset + 29]);
263 }
264 else
265 d->comment = data.mid(offset, 30);
266
267 offset += 30;
268
269 d->genre = static_cast<unsigned char>(data[offset]);
270 }
271