1 /***************************************************************************
2 copyright : (C) 2013 by Lukas Krejci
3 email : krejclu6@fel.cvut.cz
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 <tbytevectorlist.h>
27 #include <tpropertymap.h>
28 #include <tdebug.h>
29 #include <stdio.h>
30
31 #include "chapterframe.h"
32
33 using namespace TagLib;
34 using namespace ID3v2;
35
36 class ChapterFrame::ChapterFramePrivate
37 {
38 public:
ChapterFramePrivate()39 ChapterFramePrivate() :
40 tagHeader(0),
41 startTime(0),
42 endTime(0),
43 startOffset(0),
44 endOffset(0)
45 {
46 embeddedFrameList.setAutoDelete(true);
47 }
48
49 const ID3v2::Header *tagHeader;
50 ByteVector elementID;
51 unsigned int startTime;
52 unsigned int endTime;
53 unsigned int startOffset;
54 unsigned int endOffset;
55 FrameListMap embeddedFrameListMap;
56 FrameList embeddedFrameList;
57 };
58
59 ////////////////////////////////////////////////////////////////////////////////
60 // public methods
61 ////////////////////////////////////////////////////////////////////////////////
62
ChapterFrame(const ID3v2::Header * tagHeader,const ByteVector & data)63 ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) :
64 ID3v2::Frame(data),
65 d(new ChapterFramePrivate())
66 {
67 d->tagHeader = tagHeader;
68 setData(data);
69 }
70
ChapterFrame(const ByteVector & elementID,unsigned int startTime,unsigned int endTime,unsigned int startOffset,unsigned int endOffset,const FrameList & embeddedFrames)71 ChapterFrame::ChapterFrame(const ByteVector &elementID,
72 unsigned int startTime, unsigned int endTime,
73 unsigned int startOffset, unsigned int endOffset,
74 const FrameList &embeddedFrames) :
75 ID3v2::Frame("CHAP"),
76 d(new ChapterFramePrivate())
77 {
78 // setElementID has a workaround for a previously silly API where you had to
79 // specifically include the null byte.
80
81 setElementID(elementID);
82
83 d->startTime = startTime;
84 d->endTime = endTime;
85 d->startOffset = startOffset;
86 d->endOffset = endOffset;
87
88 for(FrameList::ConstIterator it = embeddedFrames.begin();
89 it != embeddedFrames.end(); ++it)
90 addEmbeddedFrame(*it);
91 }
92
~ChapterFrame()93 ChapterFrame::~ChapterFrame()
94 {
95 delete d;
96 }
97
elementID() const98 ByteVector ChapterFrame::elementID() const
99 {
100 return d->elementID;
101 }
102
startTime() const103 unsigned int ChapterFrame::startTime() const
104 {
105 return d->startTime;
106 }
107
endTime() const108 unsigned int ChapterFrame::endTime() const
109 {
110 return d->endTime;
111 }
112
startOffset() const113 unsigned int ChapterFrame::startOffset() const
114 {
115 return d->startOffset;
116 }
117
endOffset() const118 unsigned int ChapterFrame::endOffset() const
119 {
120 return d->endOffset;
121 }
122
setElementID(const ByteVector & eID)123 void ChapterFrame::setElementID(const ByteVector &eID)
124 {
125 d->elementID = eID;
126
127 if(d->elementID.endsWith(char(0)))
128 d->elementID = d->elementID.mid(0, d->elementID.size() - 1);
129 }
130
setStartTime(const unsigned int & sT)131 void ChapterFrame::setStartTime(const unsigned int &sT)
132 {
133 d->startTime = sT;
134 }
135
setEndTime(const unsigned int & eT)136 void ChapterFrame::setEndTime(const unsigned int &eT)
137 {
138 d->endTime = eT;
139 }
140
setStartOffset(const unsigned int & sO)141 void ChapterFrame::setStartOffset(const unsigned int &sO)
142 {
143 d->startOffset = sO;
144 }
145
setEndOffset(const unsigned int & eO)146 void ChapterFrame::setEndOffset(const unsigned int &eO)
147 {
148 d->endOffset = eO;
149 }
150
embeddedFrameListMap() const151 const FrameListMap &ChapterFrame::embeddedFrameListMap() const
152 {
153 return d->embeddedFrameListMap;
154 }
155
embeddedFrameList() const156 const FrameList &ChapterFrame::embeddedFrameList() const
157 {
158 return d->embeddedFrameList;
159 }
160
embeddedFrameList(const ByteVector & frameID) const161 const FrameList &ChapterFrame::embeddedFrameList(const ByteVector &frameID) const
162 {
163 return d->embeddedFrameListMap[frameID];
164 }
165
addEmbeddedFrame(Frame * frame)166 void ChapterFrame::addEmbeddedFrame(Frame *frame)
167 {
168 d->embeddedFrameList.append(frame);
169 d->embeddedFrameListMap[frame->frameID()].append(frame);
170 }
171
removeEmbeddedFrame(Frame * frame,bool del)172 void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del)
173 {
174 // remove the frame from the frame list
175 FrameList::Iterator it = d->embeddedFrameList.find(frame);
176 d->embeddedFrameList.erase(it);
177
178 // ...and from the frame list map
179 it = d->embeddedFrameListMap[frame->frameID()].find(frame);
180 d->embeddedFrameListMap[frame->frameID()].erase(it);
181
182 // ...and delete as desired
183 if(del)
184 delete frame;
185 }
186
removeEmbeddedFrames(const ByteVector & id)187 void ChapterFrame::removeEmbeddedFrames(const ByteVector &id)
188 {
189 FrameList l = d->embeddedFrameListMap[id];
190 for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
191 removeEmbeddedFrame(*it, true);
192 }
193
toString() const194 String ChapterFrame::toString() const
195 {
196 String s = String(d->elementID) +
197 ": start time: " + String::number(d->startTime) +
198 ", end time: " + String::number(d->endTime);
199
200 if(d->startOffset != 0xFFFFFFFF)
201 s += ", start offset: " + String::number(d->startOffset);
202
203 if(d->endOffset != 0xFFFFFFFF)
204 s += ", end offset: " + String::number(d->endOffset);
205
206 if(!d->embeddedFrameList.isEmpty()) {
207 StringList frameIDs;
208 for(FrameList::ConstIterator it = d->embeddedFrameList.begin();
209 it != d->embeddedFrameList.end(); ++it)
210 frameIDs.append((*it)->frameID());
211 s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]";
212 }
213
214 return s;
215 }
216
asProperties() const217 PropertyMap ChapterFrame::asProperties() const
218 {
219 PropertyMap map;
220
221 map.unsupportedData().append(frameID() + String("/") + d->elementID);
222
223 return map;
224 }
225
findByElementID(const ID3v2::Tag * tag,const ByteVector & eID)226 ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static
227 {
228 ID3v2::FrameList comments = tag->frameList("CHAP");
229
230 for(ID3v2::FrameList::ConstIterator it = comments.begin();
231 it != comments.end();
232 ++it)
233 {
234 ChapterFrame *frame = dynamic_cast<ChapterFrame *>(*it);
235 if(frame && frame->elementID() == eID)
236 return frame;
237 }
238
239 return 0;
240 }
241
parseFields(const ByteVector & data)242 void ChapterFrame::parseFields(const ByteVector &data)
243 {
244 unsigned int size = data.size();
245 if(size < 18) {
246 debug("A CHAP frame must contain at least 18 bytes (1 byte element ID "
247 "terminated by null and 4x4 bytes for start and end time and offset).");
248 return;
249 }
250
251 int pos = 0;
252 unsigned int embPos = 0;
253 d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
254 d->startTime = data.toUInt(pos, true);
255 pos += 4;
256 d->endTime = data.toUInt(pos, true);
257 pos += 4;
258 d->startOffset = data.toUInt(pos, true);
259 pos += 4;
260 d->endOffset = data.toUInt(pos, true);
261 pos += 4;
262 size -= pos;
263
264 // Embedded frames are optional
265
266 if(size < header()->size())
267 return;
268
269 while(embPos < size - header()->size()) {
270 Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0));
271
272 if(!frame)
273 return;
274
275 // Checks to make sure that frame parsed correctly.
276 if(frame->size() <= 0) {
277 delete frame;
278 return;
279 }
280
281 embPos += frame->size() + header()->size();
282 addEmbeddedFrame(frame);
283 }
284 }
285
renderFields() const286 ByteVector ChapterFrame::renderFields() const
287 {
288 ByteVector data;
289
290 data.append(d->elementID);
291 data.append('\0');
292 data.append(ByteVector::fromUInt(d->startTime, true));
293 data.append(ByteVector::fromUInt(d->endTime, true));
294 data.append(ByteVector::fromUInt(d->startOffset, true));
295 data.append(ByteVector::fromUInt(d->endOffset, true));
296 FrameList l = d->embeddedFrameList;
297 for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
298 data.append((*it)->render());
299
300 return data;
301 }
302
ChapterFrame(const ID3v2::Header * tagHeader,const ByteVector & data,Header * h)303 ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) :
304 Frame(h),
305 d(new ChapterFramePrivate())
306 {
307 d->tagHeader = tagHeader;
308 parseFields(fieldData(data));
309 }
310