id3lib 3.8.3
tag_parse.cpp
Go to the documentation of this file.
1// $Id: tag_parse.cpp,v 1.47 2002/11/24 17:33:30 t1mpy Exp $
2
3// id3lib: a C++ library for creating and manipulating id3v1/v2 tags
4// Copyright 1999, 2000 Scott Thomas Haug
5// Copyright 2002 Thijmen Klok (thijmen@id3lib.org)
6
7// This library is free software; you can redistribute it and/or modify it
8// under the terms of the GNU Library General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or (at your
10// option) any later version.
11//
12// This library is distributed in the hope that it will be useful, but WITHOUT
13// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15// License for more details.
16//
17// You should have received a copy of the GNU Library General Public License
18// along with this library; if not, write to the Free Software Foundation,
19// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21// The id3lib authors encourage improvements and optimisations to be sent to
22// the id3lib coordinator. Please see the README file for details on where to
23// send such submissions. See the AUTHORS file for a list of people who have
24// contributed to id3lib. See the ChangeLog file for a list of changes to
25// id3lib. These files are distributed with id3lib at
26// http://download.sourceforge.net/id3lib/
27
28//#if defined HAVE_CONFIG_H
29//#include <config.h> // Must include before zlib.h to compile on WinCE
30//#endif
31
32//#include <zlib.h>
33//#include <string.h>
34//#include <memory.h>
35
36#include "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h"
37//#include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h"
38#include "io_strings.h"
39
40using namespace dami;
41
42namespace
43{
44 bool parseFrames(ID3_TagImpl& tag, ID3_Reader& rdr)
45 {
46 ID3_Reader::pos_type beg = rdr.getCur();
47 io::ExitTrigger et(rdr, beg);
48 ID3_Reader::pos_type last_pos = beg;
49 size_t totalSize = 0;
50 size_t frameSize = 0;
51 while (!rdr.atEnd() && rdr.peekChar() != '\0')
52 {
53 ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getBeg() = " << rdr.getBeg() );
54 ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getCur() = " << rdr.getCur() );
55 ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getEnd() = " << rdr.getEnd() );
56 last_pos = rdr.getCur();
57 ID3_Frame* f = new ID3_Frame;
58 f->SetSpec(tag.GetSpec());
59 bool goodParse = f->Parse(rdr);
60 frameSize = rdr.getCur() - last_pos;
61 ID3D_NOTICE( "id3::v2::parseFrames(): frameSize = " << frameSize );
62 totalSize += frameSize;
63
64 if (frameSize == 0)
65 {
66 // There is a problem.
67 // If the frame size is 0, then we can't progress.
68 ID3D_WARNING( "id3::v2::parseFrames(): frame size is 0, can't " <<
69 "continue parsing frames");
70 delete f;
71 // Break for now.
72 break;
73 }
74 else if (!goodParse)
75 {
76 // bad parse! we can't attach this frame.
77 ID3D_WARNING( "id3::v2::parseFrames(): bad parse, deleting frame");
78 delete f;
79 }
80 else if (f->GetID() != ID3FID_METACOMPRESSION)
81 {
82 ID3D_NOTICE( "id3::v2::parseFrames(): attaching non-compressed " <<
83 "frame");
84 // a good, uncompressed frame. attach away!
85 tag.AttachFrame(f);
86 }
87 else
88 {
89 ID3D_NOTICE( "id3::v2::parseFrames(): parsing ID3v2.2.1 " <<
90 "compressed frame");
91 // hmm. an ID3v2.2.1 compressed frame. It contains 1 or more
92 // compressed frames. Uncompress and call parseFrames recursively.
93 ID3_Field* fld = f->GetField(ID3FN_DATA);
94 if (fld)
95 {
96 ID3_MemoryReader mr(fld->GetRawBinary(), fld->BinSize());
97 ID3_Reader::char_type ch = mr.readChar();
98 if (ch != 'z')
99 {
100 // unknown compression method
101 ID3D_WARNING( "id3::v2::parseFrames(): unknown compression id " <<
102 " = '" << ch << "'" );
103 }
104 else
105 {
106 uint32 newSize = io::readBENumber(mr, sizeof(uint32));
107 size_t oldSize = f->GetDataSize() - sizeof(uint32) - 1;
108 io::CompressedReader cr(mr, newSize);
109 parseFrames(tag, cr);
110 if (!cr.atEnd())
111 {
112 // hmm. it didn't parse the entire uncompressed data. wonder
113 // why.
114 ID3D_WARNING( "id3::v2::parseFrames(): didn't parse entire " <<
115 "id3v2.2.1 compressed memory stream");
116 }
117 }
118 }
119 delete f;
120 }
121 et.setExitPos(rdr.getCur());
122 }
123 if (rdr.peekChar() == '\0')
124 {
125 ID3D_NOTICE( "id3::v2::parseFrames: done parsing, padding at postion " <<
126 rdr.getCur() );
127 }
128 else
129 {
130 ID3D_NOTICE( "id3::v2::parseFrames: done parsing, [cur, end] = [" <<
131 rdr.getCur() << ", " << rdr.getEnd() << "]" );
132 }
133 return true;
134 }
135};
136
138{
139 ID3_Reader::pos_type beg = reader.getCur();
140 io::ExitTrigger et(reader);
141
142 ID3_TagHeader hdr;
143
144 io::WindowedReader wr(reader, ID3_TagHeader::SIZE);
145
146 if (!hdr.Parse(wr) || wr.getCur() == beg)
147 {
148 ID3D_NOTICE( "id3::v2::parse(): parsing header failes" );
149 return false;
150 }
151 if (hdr.GetExtended())
152 {
153 hdr.ParseExtended(reader);
154 }
155 tag.SetSpec(hdr.GetSpec());
156
157 size_t dataSize = hdr.GetDataSize();
158 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): dataSize = " << dataSize);
159
160 wr.setWindow(wr.getCur(), dataSize);
161 et.setExitPos(wr.getEnd());
162
163 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window beg = " << wr.getBeg() );
164 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window cur = " << wr.getCur() );
165 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window end = " << wr.getEnd() );
166 tag.SetExtended(hdr.GetExtended());
167 if (!hdr.GetUnsync())
168 {
169 tag.SetUnsync(false);
170 parseFrames(tag, wr);
171 }
172 else
173 {
174 // The buffer has been unsynced. It will have to be resynced to be
175 // readable. This has to be done a character at a time.
176 //
177 // The original reader may be reading in characters from a file. Doing
178 // this a character at a time is quite slow. To improve performance, read
179 // in the entire buffer into a string, then create an UnsyncedReader from
180 // the string.
181 //
182 // It might be better to implement a BufferedReader so that the details
183 // of this can be abstracted away behind a class
184 tag.SetUnsync(true);
185 BString raw = io::readAllBinary(wr);
186 io::BStringReader bsr(raw);
187 io::UnsyncedReader ur(bsr);
188 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync beg = " << ur.getBeg() );
189 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync cur = " << ur.getCur() );
190 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync end = " << ur.getEnd() );
191
192 // Now read the UnsyncedReader into another string, and parse the frames
193 // from the string. This is done so that 1. the unsynced reader is
194 // unsynced exactly once, removing the possibility of multiple unsyncings
195 // of the same string, and 2) so that calls to readChars aren't done a
196 // character at a time for every call
197 BString synced = io::readAllBinary(ur);
198 io::BStringReader sr(synced);
199 parseFrames(tag, sr);
200 }
201
202 return true;
203}
204
206{
207 ifstream file;
208 if (ID3E_NoError != openReadableFile(this->GetFileName(), file))
209 {
210 // log this...
211 return;
212 }
213 ID3_IFStreamReader ifsr(file);
214 ParseReader(ifsr);
215 file.close();
216}
217
218//used for streaming media
220{
221 size_t mp3_core_size;
222 size_t bytes_till_sync;
223
224 io::WindowedReader wr(reader);
225 wr.setBeg(wr.getCur());
226
227 _file_tags.clear();
228 _file_size = reader.getEnd();
229
230 ID3_Reader::pos_type beg = wr.getBeg();
231 ID3_Reader::pos_type cur = wr.getCur();
232 ID3_Reader::pos_type end = wr.getEnd();
233
234 ID3_Reader::pos_type last = cur;
235
236 if (_tags_to_parse.test(ID3TT_ID3V2))
237 {
238 do
239 {
240 last = cur;
241 // Parse tags at the beginning of the file first...
242 if (id3::v2::parse(*this, wr))
243 {
244 _file_tags.add(ID3TT_ID3V2);
245 }
246 cur = wr.getCur();
247 wr.setBeg(cur);
248 } while (!wr.atEnd() && cur > last);
249 }
250 // add silly padding outside the tag to _prepended_bytes
251 if (!wr.atEnd() && wr.peekChar() == '\0')
252 {
253 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): found padding outside tag" );
254 do
255 {
256 last = cur;
257 cur = wr.getCur() + 1;
258 wr.setBeg(cur);
259 wr.setCur(cur);
260 } while (!wr.atEnd() && cur > last && wr.peekChar() == '\0');
261 }
262 if (!wr.atEnd() && _file_size - (cur - beg) > 4 && wr.peekChar() == 255)
263 { //unfortunatly, this is necessary for finding an invalid padding
264 wr.setCur(cur + 1); //cur is known by peekChar
265 if (wr.readChar() == '\0' && wr.readChar() == '\0' && wr.peekChar() == '\0')
266 { //three empty bytes found, enough for me, this is stupid padding
267 cur += 3; //those are now allready read in (excluding the peekChar, since it will be added by do{})
268 do
269 {
270 last = cur;
271 cur = wr.getCur() + 1;
272 wr.setBeg(cur);
273 wr.setCur(cur);
274 } while (!wr.atEnd() && cur > last && wr.peekChar() == '\0');
275 }
276 else
277 wr.setCur(cur);
278 }
279 _prepended_bytes = cur - beg;
280 // go looking for the first sync byte to add to bytes_till_sync
281 // by not adding it to _prepended_bytes, we preserve this 'unknown' data
282 // The routine's only effect is helping the lib to find things as bitrate etc.
283 beg = wr.getBeg();
284 if (!wr.atEnd() && wr.peekChar() != 0xFF) //no sync byte, so, either this is not followed by a mp3 file or it's a fLaC file, or an encapsulating format, better check it
285 {
286 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): Didn't find mp3 sync byte" );
287 if ((_file_size - (cur - beg)) >= 4)
288 { //there is room to search for some kind of ID
289 unsigned char buf[5];
290 wr.readChars(buf, 4);
291 buf[4] = '\0';
292 // check for RIFF (an encapsulating format) ID
293 if (strncmp((char*)buf, "RIFF", 4) == 0 || strncmp((char*)buf, "RIFX", 4) == 0)
294 {
295 // next 4 bytes are RIFF size, skip them
296 cur = wr.getCur() + 4;
297 wr.setCur(cur);
298 // loop until first possible sync byte
299 if (!wr.atEnd() && wr.peekChar() != 0xFF)
300 {
301 do
302 {
303 last = cur;
304 cur = wr.getCur() + 1;
305 wr.setCur(cur);
306 } while (!wr.atEnd() && cur > last && wr.peekChar() != 0xFF);
307 }
308 }
309 else if (strncmp((char*)buf, "fLaC", 4) == 0)
310 { //a FLAC file, no need looking for a sync byte
311 beg = cur;
312 }
313 else
314 { //since we set the cursor 4 bytes ahead for looking for RIFF, RIFX or fLaC, better set it back
315 // but peekChar allready checked the first one, so we add one
316 cur = cur + 1;
317 wr.setCur(cur);
318 //go looking for a sync byte
319 if (!wr.atEnd() && wr.peekChar() != 0xFF) //no sync byte, we have an unknown byte
320 {
321 do
322 {
323 last = cur;
324 cur = wr.getCur() + 1;
325 wr.setCur(cur);
326 } while (!wr.atEnd() && cur > last && wr.peekChar() != 0xFF);
327 }
328 }
329 } //if ((_file_size - (cur - beg)) >= 4)
330 else
331 { //remaining size is smaller than 4 bytes, can't be useful, but leave it for now
332 beg = cur;
333 //file.close();
334 //return;
335 }
336 }
337 bytes_till_sync = cur - beg;
338
339 cur = wr.setCur(end);
340 if (_file_size > _prepended_bytes)
341 {
342 do
343 {
344 last = cur;
345 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): beg = " << wr.getBeg() );
346 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): cur = " << wr.getCur() );
347 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): end = " << wr.getEnd() );
348 // ...then the tags at the end
349 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): musicmatch? cur = " << wr.getCur() );
350 if (_tags_to_parse.test(ID3TT_MUSICMATCH) && mm::parse(*this, wr))
351 {
352 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): musicmatch! cur = " << wr.getCur() );
353 _file_tags.add(ID3TT_MUSICMATCH);
354 wr.setEnd(wr.getCur());
355 }
356 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v1? cur = " << wr.getCur() );
357 if (_tags_to_parse.test(ID3TT_LYRICS3) && lyr3::v1::parse(*this, wr))
358 {
359 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v1! cur = " << wr.getCur() );
360 _file_tags.add(ID3TT_LYRICS3);
361 wr.setEnd(wr.getCur());
362 }
363 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v2? cur = " << wr.getCur() );
364 if (_tags_to_parse.test(ID3TT_LYRICS3V2) && lyr3::v2::parse(*this, wr))
365 {
366 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v2! cur = " << wr.getCur() );
367 _file_tags.add(ID3TT_LYRICS3V2);
368 cur = wr.getCur();
369 wr.setCur(wr.getEnd());//set to end to seek id3v1 tag
370 //check for id3v1 tag and set End accordingly
371 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1? cur = " << wr.getCur() );
372 if (_tags_to_parse.test(ID3TT_ID3V1) && id3::v1::parse(*this, wr))
373 {
374 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1! cur = " << wr.getCur() );
375 _file_tags.add(ID3TT_ID3V1);
376 }
377 wr.setCur(cur);
378 wr.setEnd(cur);
379 }
380 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1? cur = " << wr.getCur() );
381 if (_tags_to_parse.test(ID3TT_ID3V1) && id3::v1::parse(*this, wr))
382 {
383 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1! cur = " << wr.getCur() );
384 wr.setEnd(wr.getCur());
385 _file_tags.add(ID3TT_ID3V1);
386 }
387 cur = wr.getCur();
388 } while (cur != last);
389 _appended_bytes = end - cur;
390
391 // Now get the mp3 header
392 mp3_core_size = (_file_size - _appended_bytes) - (_prepended_bytes + bytes_till_sync);
393 if (mp3_core_size >= 4)
394 { //it has at least the size for a mp3 header (a mp3 header is 4 bytes)
395 wr.setBeg(_prepended_bytes + bytes_till_sync);
396 wr.setCur(_prepended_bytes + bytes_till_sync);
397 wr.setEnd(_file_size - _appended_bytes);
398
399 _mp3_info = new Mp3Info;
400 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): mp3header? cur = " << wr.getCur() );
401
402 if (_mp3_info->Parse(wr, mp3_core_size))
403 {
404 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): mp3header! cur = " << wr.getCur() );
405 }
406 else
407 {
408 delete _mp3_info;
409 _mp3_info = NULL;
410 }
411 }
412 }
413 else
414 this->SetPadding(false); //no need to pad an empty file
415}
416
The representative class of an ID3v2 field.
Definition field.h:37
virtual const uchar * GetRawBinary() const =0
virtual size_t BinSize() const =0
bool clear()
Definition flags.h:46
bool test(TYPE f) const
Definition flags.h:42
bool add(TYPE f)
Definition flags.h:44
The representative class of an id3v2 frame.
ID3_FrameID GetID() const
Returns the type of frame that the object represents.
Definition frame.cpp:94
ID3_Field * GetField(ID3_FieldID name) const
Definition frame.cpp:147
bool Parse(ID3_Reader &)
Definition frame.cpp:199
bool SetSpec(ID3_V2Spec)
Definition frame.cpp:121
size_t GetDataSize() const
Definition frame.cpp:237
ID3_V2Spec GetSpec() const
Definition header.h:62
size_t GetDataSize() const
Definition header.h:71
virtual pos_type getCur()=0
Return the current position in the reader.
virtual bool atEnd()
Definition reader.h:125
virtual pos_type getEnd()
Return the ending position in the reader.
Definition reader.h:51
uint32 pos_type
Definition reader.h:38
uint8 char_type
Definition reader.h:37
virtual pos_type getBeg()
Return the beginning position in the reader.
Definition reader.h:48
virtual int_type peekChar()=0
Return the next character to be read without advancing the internal position.
bool GetUnsync() const
Definition header_tag.h:72
bool Parse(ID3_Reader &)
void ParseExtended(ID3_Reader &)
bool GetExtended() const
Definition header_tag.h:79
dami::String GetFileName() const
Definition tag_impl.h:114
bool SetSpec(ID3_V2Spec)
Definition tag_impl.cpp:225
void ParseReader(ID3_Reader &reader)
bool SetExtended(bool)
Definition tag_impl.cpp:244
bool SetPadding(bool)
Definition tag_impl.cpp:291
bool SetUnsync(bool)
Definition tag_impl.cpp:237
void ParseFile()
ID3_V2Spec GetSpec() const
Definition tag_impl.cpp:232
bool AttachFrame(ID3_Frame *)
Definition tag_impl.cpp:167
iterator end()
Definition tag_impl.h:133
bool Parse(ID3_Reader &, size_t mp3size)
#define NULL
Definition globals.h:743
@ ID3E_NoError
No error reported.
Definition globals.h:365
@ ID3FN_DATA
Data field.
Definition globals.h:203
@ ID3TT_LYRICS3
Represents a Lyrics3 tag.
Definition globals.h:179
@ ID3TT_ID3V2
Represents an id3v2 tag.
Definition globals.h:178
@ ID3TT_LYRICS3V2
Represents a Lyrics3 v2.00 tag.
Definition globals.h:180
@ ID3TT_MUSICMATCH
Represents a MusicMatch tag.
Definition globals.h:181
@ ID3TT_ID3V1
Represents an id3v1 or id3v1.1 tag.
Definition globals.h:177
@ ID3FID_METACOMPRESSION
Compressed meta frame (id3v2.2.1)
Definition globals.h:325
bool parse(ID3_TagImpl &, ID3_Reader &)
bool parse(ID3_TagImpl &tag, ID3_Reader &rdr)
bool parse(ID3_TagImpl &, ID3_Reader &)
bool parse(ID3_TagImpl &, ID3_Reader &)
bool parse(ID3_TagImpl &, ID3_Reader &)

Generated for id3lib by doxygen 1.10.0