Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

src/tag_parse.cpp

Go to the documentation of this file.
00001 // $Id: tag_parse.cpp,v 1.38 2000/10/29 01:37:29 eldamitri Exp $
00002 
00003 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
00004 // Copyright 1999, 2000  Scott Thomas Haug
00005 
00006 // This library is free software; you can redistribute it and/or modify it
00007 // under the terms of the GNU Library General Public License as published by
00008 // the Free Software Foundation; either version 2 of the License, or (at your
00009 // option) any later version.
00010 //
00011 // This library is distributed in the hope that it will be useful, but WITHOUT
00012 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00014 // License for more details.
00015 //
00016 // You should have received a copy of the GNU Library General Public License
00017 // along with this library; if not, write to the Free Software Foundation,
00018 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 
00020 // The id3lib authors encourage improvements and optimisations to be sent to
00021 // the id3lib coordinator.  Please see the README file for details on where to
00022 // send such submissions.  See the AUTHORS file for a list of people who have
00023 // contributed to id3lib.  See the ChangeLog file for a list of changes to
00024 // id3lib.  These files are distributed with id3lib at
00025 // http://download.sourceforge.net/id3lib/
00026 
00027 #if defined HAVE_CONFIG_H
00028 #include <config.h> // Must include before zlib.h to compile on WinCE
00029 #endif
00030 
00031 
00032 
00033 #include <zlib.h>
00034 #include <string.h>
00035 #include <memory.h>
00036 
00037 #include "tag_impl.h"
00038 #include "utils.h"
00039 #include "io_decorators.h"
00040 #include "io_helpers.h"
00041 #include "io_strings.h"
00042 #include "readers.h"
00043 
00044 using namespace dami;
00045 
00046 namespace
00047 {
00048   bool parseFrames(ID3_TagImpl& tag, ID3_Reader& rdr)
00049   { 
00050     ID3_Reader::pos_type beg = rdr.getCur();
00051     io::ExitTrigger et(rdr, beg);
00052     ID3_Reader::pos_type last_pos = beg;
00053     size_t totalSize = 0; 
00054     size_t frameSize = 0; 
00055     while (!rdr.atEnd() && rdr.peekChar() != '\0')
00056     { 
00057       ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getBeg() = " << rdr.getBeg() );
00058       ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getCur() = " << rdr.getCur() );
00059       ID3D_NOTICE( "id3::v2::parseFrames(): rdr.getEnd() = " << rdr.getEnd() );
00060       last_pos = rdr.getCur();
00061       ID3_Frame* f = new ID3_Frame; 
00062       f->SetSpec(tag.GetSpec());
00063       bool goodParse = f->Parse(rdr);
00064       frameSize = rdr.getCur() - last_pos;
00065       ID3D_NOTICE( "id3::v2::parseFrames(): frameSize = " << frameSize );
00066       totalSize += frameSize;
00067       
00068       if (frameSize == 0)
00069       { 
00070         // There is a problem. 
00071         // If the frame size is 0, then we can't progress. 
00072         ID3D_WARNING( "id3::v2::parseFrames(): frame size is 0, can't " <<
00073                       "continue parsing frames");
00074         delete f; 
00075         // Break for now. 
00076         break; 
00077       } 
00078       else if (!goodParse) 
00079       { 
00080         // bad parse!  we can't attach this frame.
00081         ID3D_WARNING( "id3::v2::parseFrames(): bad parse, deleting frame");
00082         delete f; 
00083       } 
00084       else if (f->GetID() != ID3FID_METACOMPRESSION) 
00085       { 
00086         ID3D_NOTICE( "id3::v2::parseFrames(): attaching non-compressed " <<
00087                      "frame");
00088         // a good, uncompressed frame.  attach away! 
00089         tag.AttachFrame(f); 
00090       } 
00091       else 
00092       { 
00093         ID3D_NOTICE( "id3::v2::parseFrames(): parsing ID3v2.2.1 " <<
00094                      "compressed frame");
00095         // hmm.  an ID3v2.2.1 compressed frame.  It contains 1 or more
00096         // compressed frames.  Uncompress and call parseFrames recursively.
00097         ID3_Field* fld = f->GetField(ID3FN_DATA);
00098         if (fld)
00099         {
00100           ID3_MemoryReader mr(fld->GetRawBinary(), fld->BinSize());
00101           ID3_Reader::char_type ch = mr.readChar();
00102           if (ch != 'z') 
00103           { 
00104             // unknown compression method 
00105             ID3D_WARNING( "id3::v2::parseFrames(): unknown compression id " <<
00106                           " = '" << ch << "'" );
00107           } 
00108           else 
00109           { 
00110             uint32 newSize = io::readBENumber(mr, sizeof(uint32));
00111             size_t oldSize = f->GetDataSize() - sizeof(uint32) - 1;
00112             io::CompressedReader cr(mr, newSize);
00113             parseFrames(tag, cr);
00114             if (!cr.atEnd())
00115             {
00116               // hmm.  it didn't parse the entire uncompressed data.  wonder
00117               // why.
00118               ID3D_WARNING( "id3::v2::parseFrames(): didn't parse entire " <<
00119                             "id3v2.2.1 compressed memory stream");
00120             }
00121           }
00122         }
00123         delete f;
00124       }
00125       et.setExitPos(rdr.getCur());
00126     } 
00127     if (rdr.peekChar() == '\0')
00128     {
00129       ID3D_NOTICE( "id3::v2::parseFrames: done parsing, padding at postion " << 
00130                    rdr.getCur() );
00131     }
00132     else
00133     {
00134       ID3D_NOTICE( "id3::v2::parseFrames: done parsing, [cur, end] = [" << 
00135                    rdr.getCur() << ", " << rdr.getEnd() << "]" );
00136     }
00137     return true;
00138   }
00139 };
00140  
00141 bool id3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader)
00142 {
00143   ID3_Reader::pos_type beg = reader.getCur();
00144   io::ExitTrigger et(reader);
00145   
00146   ID3_TagHeader hdr;
00147 
00148   io::WindowedReader wr(reader, ID3_TagHeader::SIZE);
00149   
00150   if (!hdr.Parse(wr) || wr.getCur() == beg)
00151   {
00152     ID3D_NOTICE( "id3::v2::parse(): parsing header failes" );
00153     return false;
00154   }
00155   
00156   tag.SetSpec(hdr.GetSpec());
00157 
00158   size_t dataSize = hdr.GetDataSize();
00159   ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): dataSize = " << dataSize);
00160 
00161   wr.setWindow(wr.getCur(), dataSize);
00162   et.setExitPos(wr.getEnd());
00163 
00164   ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window beg = " << wr.getBeg() );
00165   ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window cur = " << wr.getCur() );
00166   ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window end = " << wr.getEnd() );
00167   tag.SetExtended(hdr.GetExtended());
00168   if (!hdr.GetUnsync())
00169   {
00170     tag.SetUnsync(false);
00171     parseFrames(tag, wr);
00172   }
00173   else
00174   {
00175     // The buffer has been unsynced.  It will have to be resynced to be 
00176     // readable.  This has to be done a character at a time.  
00177     //
00178     // The original reader may be reading in characters from a file.  Doing
00179     // this a character at a time is quite slow.  To improve performance, read
00180     // in the entire buffer into a string, then create an UnsyncedReader from
00181     // the string.
00182     //
00183     // It might be better to implement a BufferedReader so that the details
00184     // of this can be abstracted away behind a class
00185     tag.SetUnsync(true);
00186     BString raw = io::readAllBinary(wr);
00187     io::BStringReader bsr(raw);
00188     io::UnsyncedReader ur(bsr);
00189     ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync beg = " << ur.getBeg() );
00190     ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync cur = " << ur.getCur() );
00191     ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync end = " << ur.getEnd() );
00192 
00193     // Now read the UnsyncedReader into another string, and parse the frames
00194     // from the string.  This is done so that 1. the unsynced reader is 
00195     // unsynced exactly once, removing the possibility of multiple unsyncings
00196     // of the same string, and 2) so that calls to readChars aren't done a 
00197     // character at a time for every call
00198     BString synced = io::readAllBinary(ur);
00199     io::BStringReader sr(synced);
00200     parseFrames(tag, sr);
00201   }
00202 
00203   return true;
00204 }
00205 
00206 void ID3_TagImpl::ParseFile()
00207 {
00208   ifstream file;
00209   if (ID3E_NoError != openReadableFile(this->GetFileName(), file))
00210   {
00211     // log this...
00212     return;
00213   }
00214   ID3_IFStreamReader ifsr(file);
00215   io::WindowedReader wr(ifsr);
00216   wr.setBeg(wr.getCur());
00217 
00218   _file_tags.clear();
00219   _file_size = getFileSize(file);
00220 
00221   ID3_Reader::pos_type beg  = wr.getBeg();
00222   ID3_Reader::pos_type cur  = wr.getCur();
00223   ID3_Reader::pos_type end  = wr.getEnd();
00224 
00225   ID3_Reader::pos_type last = cur;
00226 
00227   if (_tags_to_parse.test(ID3TT_ID3V2))
00228   {
00229     do
00230     {
00231       last = cur;
00232       // Parse tags at the beginning of the file first...
00233       if (id3::v2::parse(*this, wr))
00234       {
00235         _file_tags.add(ID3TT_ID3V2);
00236       }
00237       cur  = wr.getCur();
00238       wr.setBeg(cur);
00239     } while (!wr.atEnd() && cur > last);
00240   }
00241 
00242   _prepended_bytes = cur - beg;
00243 
00244   cur = wr.setCur(end);
00245   do
00246   {
00247     last = cur;
00248     ID3D_NOTICE( "ID3_TagImpl::ParseFile(): beg = " << wr.getBeg() );
00249     ID3D_NOTICE( "ID3_TagImpl::ParseFile(): cur = " << wr.getCur() );
00250     ID3D_NOTICE( "ID3_TagImpl::ParseFile(): end = " << wr.getEnd() );
00251     // ...then the tags at the end
00252     ID3D_NOTICE( "ID3_TagImpl::ParseFile(): musicmatch? cur = " << wr.getCur() );
00253     if (_tags_to_parse.test(ID3TT_MUSICMATCH) && mm::parse(*this, wr))
00254     {
00255       ID3D_NOTICE( "ID3_TagImpl::ParseFile(): musicmatch! cur = " << wr.getCur() );
00256       _file_tags.add(ID3TT_MUSICMATCH);
00257       wr.setEnd(wr.getCur());
00258     }
00259     ID3D_NOTICE( "ID3_TagImpl::ParseFile(): lyr3v1? cur = " << wr.getCur() );
00260     if (_tags_to_parse.test(ID3TT_LYRICS3) && lyr3::v1::parse(*this, wr))
00261     {
00262       ID3D_NOTICE( "ID3_TagImpl::ParseFile(): lyr3v1! cur = " << wr.getCur() );
00263       _file_tags.add(ID3TT_LYRICS3);
00264       wr.setEnd(wr.getCur());
00265     }
00266     ID3D_NOTICE( "ID3_TagImpl::ParseFile(): lyr3v2? cur = " << wr.getCur() );
00267     if (_tags_to_parse.test(ID3TT_LYRICS3V2) && lyr3::v2::parse(*this, wr))
00268     {
00269       ID3D_NOTICE( "ID3_TagImpl::ParseFile(): lyr3v2! cur = " << wr.getCur() );
00270       _file_tags.add(ID3TT_ID3V1);
00271       wr.setEnd(wr.getCur());
00272     }
00273     ID3D_NOTICE( "ID3_TagImpl::ParseFile(): id3v1? cur = " << wr.getCur() );
00274     if (_tags_to_parse.test(ID3TT_ID3V1) && id3::v1::parse(*this, wr))
00275     {
00276       ID3D_NOTICE( "ID3_TagImpl::ParseFile(): id3v1! cur = " << wr.getCur() );
00277       wr.setEnd(wr.getCur());
00278       _file_tags.add(ID3TT_ID3V1);
00279     }
00280     cur = wr.getCur();
00281   } while (cur != last);
00282   _appended_bytes = end - cur;
00283 }

Generated at Sat Sep 8 15:51:10 2001 for id3lib by doxygen1.2.8 written by Dimitri van Heesch, © 1997-2001