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