1 /***************************************************************************
2 copyright : (C) 2012 by Tsuda Kageyu
3 email : tsuda.kageyu@gmail.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., 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 "infotag.h"
30 #include "riffutils.h"
31
32 using namespace TagLib;
33 using namespace RIFF::Info;
34
35 namespace
36 {
37 const RIFF::Info::StringHandler defaultStringHandler;
38 const RIFF::Info::StringHandler *stringHandler = &defaultStringHandler;
39 }
40
41 class RIFF::Info::Tag::TagPrivate
42 {
43 public:
44 FieldListMap fieldListMap;
45 };
46
47 ////////////////////////////////////////////////////////////////////////////////
48 // StringHandler implementation
49 ////////////////////////////////////////////////////////////////////////////////
50
StringHandler()51 StringHandler::StringHandler()
52 {
53 }
54
~StringHandler()55 StringHandler::~StringHandler()
56 {
57 }
58
parse(const ByteVector & data) const59 String RIFF::Info::StringHandler::parse(const ByteVector &data) const
60 {
61 return String(data, String::UTF8);
62 }
63
render(const String & s) const64 ByteVector RIFF::Info::StringHandler::render(const String &s) const
65 {
66 return s.data(String::UTF8);
67 }
68
69 ////////////////////////////////////////////////////////////////////////////////
70 // public members
71 ////////////////////////////////////////////////////////////////////////////////
72
Tag(const ByteVector & data)73 RIFF::Info::Tag::Tag(const ByteVector &data) :
74 TagLib::Tag(),
75 d(new TagPrivate())
76 {
77 parse(data);
78 }
79
Tag()80 RIFF::Info::Tag::Tag() :
81 TagLib::Tag(),
82 d(new TagPrivate())
83 {
84 }
85
~Tag()86 RIFF::Info::Tag::~Tag()
87 {
88 delete d;
89 }
90
title() const91 String RIFF::Info::Tag::title() const
92 {
93 return fieldText("INAM");
94 }
95
artist() const96 String RIFF::Info::Tag::artist() const
97 {
98 return fieldText("IART");
99 }
100
album() const101 String RIFF::Info::Tag::album() const
102 {
103 return fieldText("IPRD");
104 }
105
comment() const106 String RIFF::Info::Tag::comment() const
107 {
108 return fieldText("ICMT");
109 }
110
genre() const111 String RIFF::Info::Tag::genre() const
112 {
113 return fieldText("IGNR");
114 }
115
year() const116 unsigned int RIFF::Info::Tag::year() const
117 {
118 return fieldText("ICRD").substr(0, 4).toInt();
119 }
120
track() const121 unsigned int RIFF::Info::Tag::track() const
122 {
123 return fieldText("IPRT").toInt();
124 }
125
setTitle(const String & s)126 void RIFF::Info::Tag::setTitle(const String &s)
127 {
128 setFieldText("INAM", s);
129 }
130
setArtist(const String & s)131 void RIFF::Info::Tag::setArtist(const String &s)
132 {
133 setFieldText("IART", s);
134 }
135
setAlbum(const String & s)136 void RIFF::Info::Tag::setAlbum(const String &s)
137 {
138 setFieldText("IPRD", s);
139 }
140
setComment(const String & s)141 void RIFF::Info::Tag::setComment(const String &s)
142 {
143 setFieldText("ICMT", s);
144 }
145
setGenre(const String & s)146 void RIFF::Info::Tag::setGenre(const String &s)
147 {
148 setFieldText("IGNR", s);
149 }
150
setYear(unsigned int i)151 void RIFF::Info::Tag::setYear(unsigned int i)
152 {
153 if(i != 0)
154 setFieldText("ICRD", String::number(i));
155 else
156 d->fieldListMap.erase("ICRD");
157 }
158
setTrack(unsigned int i)159 void RIFF::Info::Tag::setTrack(unsigned int i)
160 {
161 if(i != 0)
162 setFieldText("IPRT", String::number(i));
163 else
164 d->fieldListMap.erase("IPRT");
165 }
166
isEmpty() const167 bool RIFF::Info::Tag::isEmpty() const
168 {
169 return d->fieldListMap.isEmpty();
170 }
171
fieldListMap() const172 FieldListMap RIFF::Info::Tag::fieldListMap() const
173 {
174 return d->fieldListMap;
175 }
176
fieldText(const ByteVector & id) const177 String RIFF::Info::Tag::fieldText(const ByteVector &id) const
178 {
179 if(d->fieldListMap.contains(id))
180 return String(d->fieldListMap[id]);
181 else
182 return String();
183 }
184
setFieldText(const ByteVector & id,const String & s)185 void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s)
186 {
187 // id must be four-byte long pure ascii string.
188 if(!isValidChunkName(id))
189 return;
190
191 if(!s.isEmpty())
192 d->fieldListMap[id] = s;
193 else
194 removeField(id);
195 }
196
removeField(const ByteVector & id)197 void RIFF::Info::Tag::removeField(const ByteVector &id)
198 {
199 if(d->fieldListMap.contains(id))
200 d->fieldListMap.erase(id);
201 }
202
render() const203 ByteVector RIFF::Info::Tag::render() const
204 {
205 ByteVector data("INFO");
206
207 FieldListMap::ConstIterator it = d->fieldListMap.begin();
208 for(; it != d->fieldListMap.end(); ++it) {
209 ByteVector text = stringHandler->render(it->second);
210 if(text.isEmpty())
211 continue;
212
213 data.append(it->first);
214 data.append(ByteVector::fromUInt(text.size() + 1, false));
215 data.append(text);
216
217 do {
218 data.append('\0');
219 } while(data.size() & 1);
220 }
221
222 if(data.size() == 4)
223 return ByteVector();
224 else
225 return data;
226 }
227
setStringHandler(const StringHandler * handler)228 void RIFF::Info::Tag::setStringHandler(const StringHandler *handler)
229 {
230 if(handler)
231 stringHandler = handler;
232 else
233 stringHandler = &defaultStringHandler;
234 }
235
236 ////////////////////////////////////////////////////////////////////////////////
237 // protected members
238 ////////////////////////////////////////////////////////////////////////////////
239
parse(const ByteVector & data)240 void RIFF::Info::Tag::parse(const ByteVector &data)
241 {
242 unsigned int p = 4;
243 while(p < data.size()) {
244 const unsigned int size = data.toUInt(p + 4, false);
245 if(size > data.size() - p - 8)
246 break;
247
248 const ByteVector id = data.mid(p, 4);
249 if(isValidChunkName(id)) {
250 const String text = stringHandler->parse(data.mid(p + 8, size));
251 d->fieldListMap[id] = text;
252 }
253
254 p += ((size + 1) & ~1) + 8;
255 }
256 }
257