id3lib 3.8.3

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
40 using namespace dami;
41
42 namespace
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
137 bool id3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader)
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
205 void ID3_TagImpl::ParseFile()
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
219 void ID3_TagImpl::ParseReader(ID3_Reader &reader)
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
ID3_Field::GetRawBinary
virtual const uchar * GetRawBinary() const =0
ID3_Reader::getBeg
virtual pos_type getBeg()
Return the beginning position in the reader.
Definition: reader.h:48
ID3_TagImpl::ParseFile
void ParseFile()
Definition: tag_parse.cpp:205
dami::mm::parse
bool parse(ID3_TagImpl &, ID3_Reader &)
Definition: tag_parse_musicmatch.cpp:115
ID3_IFStreamReader
Definition: readers.h:75
ID3_TagImpl
Definition: tag_impl.h:73
ID3TT_LYRICS3
Represents a Lyrics3 tag.
Definition: globals.h:179
ID3_Frame::GetField
ID3_Field * GetField(ID3_FieldID name) const
Definition: frame.cpp:147
ID3E_NoError
No error reported.
Definition: globals.h:365
ID3_TagHeader::GetExtended
bool GetExtended() const
Definition: header_tag.h:79
ID3TT_LYRICS3V2
Represents a Lyrics3 v2.00 tag.
Definition: globals.h:180
ID3_TagHeader::GetUnsync
bool GetUnsync() const
Definition: header_tag.h:72
ID3_Reader::getCur
virtual pos_type getCur()=0
Return the current position in the reader.
ID3_TagHeader::SIZE
Definition: header_tag.h:108
ID3_Frame::GetID
ID3_FrameID GetID() const
Returns the type of frame that the object represents.
Definition: frame.cpp:94
ID3_Header::GetDataSize
size_t GetDataSize() const
Definition: header.h:71
ID3_TagImpl::SetExtended
bool SetExtended(bool)
Definition: tag_impl.cpp:244
ID3_TagHeader
Definition: header_tag.h:34
ID3_Reader::peekChar
virtual int_type peekChar()=0
Return the next character to be read without advancing the internal position.
dami::id3::v2::parse
bool parse(ID3_TagImpl &tag, ID3_Reader &rdr)
Definition: tag_parse.cpp:137
dami::id3::v1::parse
bool parse(ID3_TagImpl &, ID3_Reader &)
Definition: tag_parse_v1.cpp:35
dami::BString
std::basic_string< unsigned char > BString
Definition: id3lib_strings.h:199
ID3_TagHeader::ParseExtended
void ParseExtended(ID3_Reader &)
Definition: header_tag.cpp:134
ID3_Reader::readChar
virtual int_type readChar()
Read a single character and advance the internal position.
Definition: reader.h:65
ID3_Reader
Definition: reader.h:33
ID3TT_MUSICMATCH
Represents a MusicMatch tag.
Definition: globals.h:181
ID3_Frame::SetSpec
bool SetSpec(ID3_V2Spec)
Definition: frame.cpp:121
tag_impl.h
ID3_TagImpl::AttachFrame
bool AttachFrame(ID3_Frame *)
Definition: tag_impl.cpp:167
dami::openReadableFile
ID3_Err ID3_C_EXPORT openReadableFile(String, fstream &)
Definition: utils.cpp:335
ID3_Reader::getEnd
virtual pos_type getEnd()
Return the ending position in the reader.
Definition: reader.h:51
dami::lyr3::v2::parse
bool parse(ID3_TagImpl &, ID3_Reader &)
Definition: tag_parse_lyrics3.cpp:222
ID3_TagHeader::Parse
bool Parse(ID3_Reader &)
Definition: header_tag.cpp:101
ID3_Field::BinSize
virtual size_t BinSize() const =0
ID3_Header::GetSpec
ID3_V2Spec GetSpec() const
Definition: header.h:62
ID3_TagImpl::GetSpec
ID3_V2Spec GetSpec() const
Definition: tag_impl.cpp:232
ID3_Frame
The representative class of an id3v2 frame.
Definition: id3lib_frame.h:38
ID3TT_ID3V2
Represents an id3v2 tag.
Definition: globals.h:178
dami::lyr3::v1::parse
bool parse(ID3_TagImpl &, ID3_Reader &)
Definition: tag_parse_lyrics3.cpp:171
ID3_Frame::Parse
bool Parse(ID3_Reader &)
Definition: frame.cpp:199
ID3FN_DATA
Data field.
Definition: globals.h:203
ID3_TagImpl::ParseReader
void ParseReader(ID3_Reader &reader)
Definition: tag_parse.cpp:219
NULL
#define NULL
Definition: globals.h:743
ID3_Reader::atEnd
virtual bool atEnd()
Definition: reader.h:125
ID3FID_METACOMPRESSION
Compressed meta frame (id3v2.2.1)
Definition: globals.h:325
ID3_Field
The representative class of an ID3v2 field.
Definition: field.h:36
ID3_Reader::pos_type
uint32 pos_type
Definition: reader.h:38
ID3_MemoryReader
Definition: readers.h:88
ID3_Frame::GetDataSize
size_t GetDataSize() const
Definition: frame.cpp:237
ID3_Reader::char_type
uint8 char_type
Definition: reader.h:37
Mp3Info
Definition: mp3_header.h:33
ID3_TagImpl::SetUnsync
bool SetUnsync(bool)
Definition: tag_impl.cpp:237
ID3TT_ID3V1
Represents an id3v1 or id3v1.1 tag.
Definition: globals.h:177
ID3_TagImpl::SetSpec
bool SetSpec(ID3_V2Spec)
Definition: tag_impl.cpp:225