00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "tag_impl.h"
00037
00038 #include "io_strings.h"
00039
00040 using namespace dami;
00041
00042 namespace
00043 {
00044 bool parseFrames(ID3_TagImpl& tag, ID3_Reader& rdr)
00045 {
00046 ID3_Reader::pos_type beg = rdr.getCur();
00047 io::ExitTrigger et(rdr, beg);
00048 ID3_Reader::pos_type last_pos = beg;
00049 size_t totalSize = 0;
00050 size_t frameSize = 0;
00051 while (!rdr.atEnd() && rdr.peekChar() != '\0')
00052 {
00053 ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getBeg() = " << rdr.getBeg() );
00054 ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getCur() = " << rdr.getCur() );
00055 ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getEnd() = " << rdr.getEnd() );
00056 last_pos = rdr.getCur();
00057 ID3_Frame* f = new ID3_Frame;
00058 f->SetSpec(tag.GetSpec());
00059 bool goodParse = f->Parse(rdr);
00060 frameSize = rdr.getCur() - last_pos;
00061 ID3D_NOTICE( "id3::v2::parseFrames(): frameSize = " << frameSize );
00062 totalSize += frameSize;
00063
00064 if (frameSize == 0)
00065 {
00066
00067
00068 ID3D_WARNING( "id3::v2::parseFrames(): frame size is 0, can't " <<
00069 "continue parsing frames");
00070 delete f;
00071
00072 break;
00073 }
00074 else if (!goodParse)
00075 {
00076
00077 ID3D_WARNING( "id3::v2::parseFrames(): bad parse, deleting frame");
00078 delete f;
00079 }
00080 else if (f->GetID() != ID3FID_METACOMPRESSION)
00081 {
00082 ID3D_NOTICE( "id3::v2::parseFrames(): attaching non-compressed " <<
00083 "frame");
00084
00085 tag.AttachFrame(f);
00086 }
00087 else
00088 {
00089 ID3D_NOTICE( "id3::v2::parseFrames(): parsing ID3v2.2.1 " <<
00090 "compressed frame");
00091
00092
00093 ID3_Field* fld = f->GetField(ID3FN_DATA);
00094 if (fld)
00095 {
00096 ID3_MemoryReader mr(fld->GetRawBinary(), fld->BinSize());
00097 ID3_Reader::char_type ch = mr.readChar();
00098 if (ch != 'z')
00099 {
00100
00101 ID3D_WARNING( "id3::v2::parseFrames(): unknown compression id " <<
00102 " = '" << ch << "'" );
00103 }
00104 else
00105 {
00106 uint32 newSize = io::readBENumber(mr, sizeof(uint32));
00107 size_t oldSize = f->GetDataSize() - sizeof(uint32) - 1;
00108 io::CompressedReader cr(mr, newSize);
00109 parseFrames(tag, cr);
00110 if (!cr.atEnd())
00111 {
00112
00113
00114 ID3D_WARNING( "id3::v2::parseFrames(): didn't parse entire " <<
00115 "id3v2.2.1 compressed memory stream");
00116 }
00117 }
00118 }
00119 delete f;
00120 }
00121 et.setExitPos(rdr.getCur());
00122 }
00123 if (rdr.peekChar() == '\0')
00124 {
00125 ID3D_NOTICE( "id3::v2::parseFrames: done parsing, padding at postion " <<
00126 rdr.getCur() );
00127 }
00128 else
00129 {
00130 ID3D_NOTICE( "id3::v2::parseFrames: done parsing, [cur, end] = [" <<
00131 rdr.getCur() << ", " << rdr.getEnd() << "]" );
00132 }
00133 return true;
00134 }
00135 };
00136
00137 bool id3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader)
00138 {
00139 ID3_Reader::pos_type beg = reader.getCur();
00140 io::ExitTrigger et(reader);
00141
00142 ID3_TagHeader hdr;
00143
00144 io::WindowedReader wr(reader, ID3_TagHeader::SIZE);
00145
00146 if (!hdr.Parse(wr) || wr.getCur() == beg)
00147 {
00148 ID3D_NOTICE( "id3::v2::parse(): parsing header failes" );
00149 return false;
00150 }
00151 if (hdr.GetExtended())
00152 {
00153 hdr.ParseExtended(reader);
00154 }
00155 tag.SetSpec(hdr.GetSpec());
00156
00157 size_t dataSize = hdr.GetDataSize();
00158 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): dataSize = " << dataSize);
00159
00160 wr.setWindow(wr.getCur(), dataSize);
00161 et.setExitPos(wr.getEnd());
00162
00163 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window beg = " << wr.getBeg() );
00164 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window cur = " << wr.getCur() );
00165 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window end = " << wr.getEnd() );
00166 tag.SetExtended(hdr.GetExtended());
00167 if (!hdr.GetUnsync())
00168 {
00169 tag.SetUnsync(false);
00170 parseFrames(tag, wr);
00171 }
00172 else
00173 {
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 tag.SetUnsync(true);
00185 BString raw = io::readAllBinary(wr);
00186 io::BStringReader bsr(raw);
00187 io::UnsyncedReader ur(bsr);
00188 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync beg = " << ur.getBeg() );
00189 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync cur = " << ur.getCur() );
00190 ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync end = " << ur.getEnd() );
00191
00192
00193
00194
00195
00196
00197 BString synced = io::readAllBinary(ur);
00198 io::BStringReader sr(synced);
00199 parseFrames(tag, sr);
00200 }
00201
00202 return true;
00203 }
00204
00205 void ID3_TagImpl::ParseFile()
00206 {
00207 ifstream file;
00208 if (ID3E_NoError != openReadableFile(this->GetFileName(), file))
00209 {
00210
00211 return;
00212 }
00213 ID3_IFStreamReader ifsr(file);
00214 ParseReader(ifsr);
00215 file.close();
00216 }
00217
00218
00219 void ID3_TagImpl::ParseReader(ID3_Reader &reader)
00220 {
00221 size_t mp3_core_size;
00222 size_t bytes_till_sync;
00223
00224 io::WindowedReader wr(reader);
00225 wr.setBeg(wr.getCur());
00226
00227 _file_tags.clear();
00228 _file_size = reader.getEnd();
00229
00230 ID3_Reader::pos_type beg = wr.getBeg();
00231 ID3_Reader::pos_type cur = wr.getCur();
00232 ID3_Reader::pos_type end = wr.getEnd();
00233
00234 ID3_Reader::pos_type last = cur;
00235
00236 if (_tags_to_parse.test(ID3TT_ID3V2))
00237 {
00238 do
00239 {
00240 last = cur;
00241
00242 if (id3::v2::parse(*this, wr))
00243 {
00244 _file_tags.add(ID3TT_ID3V2);
00245 }
00246 cur = wr.getCur();
00247 wr.setBeg(cur);
00248 } while (!wr.atEnd() && cur > last);
00249 }
00250
00251 if (!wr.atEnd() && wr.peekChar() == '\0')
00252 {
00253 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): found padding outside tag" );
00254 do
00255 {
00256 last = cur;
00257 cur = wr.getCur() + 1;
00258 wr.setBeg(cur);
00259 wr.setCur(cur);
00260 } while (!wr.atEnd() && cur > last && wr.peekChar() == '\0');
00261 }
00262 if (!wr.atEnd() && _file_size - (cur - beg) > 4 && wr.peekChar() == 255)
00263 {
00264 wr.setCur(cur + 1);
00265 if (wr.readChar() == '\0' && wr.readChar() == '\0' && wr.peekChar() == '\0')
00266 {
00267 cur += 3;
00268 do
00269 {
00270 last = cur;
00271 cur = wr.getCur() + 1;
00272 wr.setBeg(cur);
00273 wr.setCur(cur);
00274 } while (!wr.atEnd() && cur > last && wr.peekChar() == '\0');
00275 }
00276 else
00277 wr.setCur(cur);
00278 }
00279 _prepended_bytes = cur - beg;
00280
00281
00282
00283 beg = wr.getBeg();
00284 if (!wr.atEnd() && wr.peekChar() != 0xFF)
00285 {
00286 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): Didn't find mp3 sync byte" );
00287 if ((_file_size - (cur - beg)) >= 4)
00288 {
00289 unsigned char buf[5];
00290 wr.readChars(buf, 4);
00291 buf[4] = '\0';
00292
00293 if (strncmp((char*)buf, "RIFF", 4) == 0 || strncmp((char*)buf, "RIFX", 4) == 0)
00294 {
00295
00296 cur = wr.getCur() + 4;
00297 wr.setCur(cur);
00298
00299 if (!wr.atEnd() && wr.peekChar() != 0xFF)
00300 {
00301 do
00302 {
00303 last = cur;
00304 cur = wr.getCur() + 1;
00305 wr.setCur(cur);
00306 } while (!wr.atEnd() && cur > last && wr.peekChar() != 0xFF);
00307 }
00308 }
00309 else if (strncmp((char*)buf, "fLaC", 4) == 0)
00310 {
00311 beg = cur;
00312 }
00313 else
00314 {
00315
00316 cur = cur + 1;
00317 wr.setCur(cur);
00318
00319 if (!wr.atEnd() && wr.peekChar() != 0xFF)
00320 {
00321 do
00322 {
00323 last = cur;
00324 cur = wr.getCur() + 1;
00325 wr.setCur(cur);
00326 } while (!wr.atEnd() && cur > last && wr.peekChar() != 0xFF);
00327 }
00328 }
00329 }
00330 else
00331 {
00332 beg = cur;
00333
00334
00335 }
00336 }
00337 bytes_till_sync = cur - beg;
00338
00339 cur = wr.setCur(end);
00340 if (_file_size > _prepended_bytes)
00341 {
00342 do
00343 {
00344 last = cur;
00345 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): beg = " << wr.getBeg() );
00346 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): cur = " << wr.getCur() );
00347 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): end = " << wr.getEnd() );
00348
00349 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): musicmatch? cur = " << wr.getCur() );
00350 if (_tags_to_parse.test(ID3TT_MUSICMATCH) && mm::parse(*this, wr))
00351 {
00352 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): musicmatch! cur = " << wr.getCur() );
00353 _file_tags.add(ID3TT_MUSICMATCH);
00354 wr.setEnd(wr.getCur());
00355 }
00356 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v1? cur = " << wr.getCur() );
00357 if (_tags_to_parse.test(ID3TT_LYRICS3) && lyr3::v1::parse(*this, wr))
00358 {
00359 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v1! cur = " << wr.getCur() );
00360 _file_tags.add(ID3TT_LYRICS3);
00361 wr.setEnd(wr.getCur());
00362 }
00363 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v2? cur = " << wr.getCur() );
00364 if (_tags_to_parse.test(ID3TT_LYRICS3V2) && lyr3::v2::parse(*this, wr))
00365 {
00366 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): lyr3v2! cur = " << wr.getCur() );
00367 _file_tags.add(ID3TT_LYRICS3V2);
00368 cur = wr.getCur();
00369 wr.setCur(wr.getEnd());
00370
00371 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1? cur = " << wr.getCur() );
00372 if (_tags_to_parse.test(ID3TT_ID3V1) && id3::v1::parse(*this, wr))
00373 {
00374 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1! cur = " << wr.getCur() );
00375 _file_tags.add(ID3TT_ID3V1);
00376 }
00377 wr.setCur(cur);
00378 wr.setEnd(cur);
00379 }
00380 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1? cur = " << wr.getCur() );
00381 if (_tags_to_parse.test(ID3TT_ID3V1) && id3::v1::parse(*this, wr))
00382 {
00383 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): id3v1! cur = " << wr.getCur() );
00384 wr.setEnd(wr.getCur());
00385 _file_tags.add(ID3TT_ID3V1);
00386 }
00387 cur = wr.getCur();
00388 } while (cur != last);
00389 _appended_bytes = end - cur;
00390
00391
00392 mp3_core_size = (_file_size - _appended_bytes) - (_prepended_bytes + bytes_till_sync);
00393 if (mp3_core_size >= 4)
00394 {
00395 wr.setBeg(_prepended_bytes + bytes_till_sync);
00396 wr.setCur(_prepended_bytes + bytes_till_sync);
00397 wr.setEnd(_file_size - _appended_bytes);
00398
00399 _mp3_info = new Mp3Info;
00400 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): mp3header? cur = " << wr.getCur() );
00401
00402 if (_mp3_info->Parse(wr, mp3_core_size))
00403 {
00404 ID3D_NOTICE( "ID3_TagImpl::ParseReader(): mp3header! cur = " << wr.getCur() );
00405 }
00406 else
00407 {
00408 delete _mp3_info;
00409 _mp3_info = NULL;
00410 }
00411 }
00412 }
00413 else
00414 this->SetPadding(false);
00415 }
00416