Added TagLib (with AUTORS and COPYING files) 1.2.0
authorNikolay Tischenko <niktischenko@gmail.com>
Fri, 24 Sep 2010 16:09:14 +0000 (23:09 +0700)
committerNikolay Tischenko <niktischenko@gmail.com>
Fri, 24 Sep 2010 16:09:14 +0000 (23:09 +0700)
Switched to TagLib for tag resolving
Added progressbar to busy widget

166 files changed:
AUTHORS.TagLib [new file with mode: 0644]
COPYING.LGPL [new file with mode: 0644]
resources/someplayer.png
someplayer.pro
src/busywidget.cpp
src/busywidget.h
src/library.cpp
src/library.h
src/mainwindow.cpp
src/mediascanner.cpp
src/someplayer.h
src/taglib/ape/ape-tag-format.txt [new file with mode: 0644]
src/taglib/ape/apefooter.cpp [new file with mode: 0644]
src/taglib/ape/apefooter.h [new file with mode: 0644]
src/taglib/ape/apeitem.cpp [new file with mode: 0644]
src/taglib/ape/apeitem.h [new file with mode: 0644]
src/taglib/ape/apetag.cpp [new file with mode: 0644]
src/taglib/ape/apetag.h [new file with mode: 0644]
src/taglib/asf/asfattribute.cpp [new file with mode: 0644]
src/taglib/asf/asfattribute.h [new file with mode: 0644]
src/taglib/asf/asffile.cpp [new file with mode: 0644]
src/taglib/asf/asffile.h [new file with mode: 0644]
src/taglib/asf/asfproperties.cpp [new file with mode: 0644]
src/taglib/asf/asfproperties.h [new file with mode: 0644]
src/taglib/asf/asftag.cpp [new file with mode: 0644]
src/taglib/asf/asftag.h [new file with mode: 0644]
src/taglib/audioproperties.cpp [new file with mode: 0644]
src/taglib/audioproperties.h [new file with mode: 0644]
src/taglib/fileref.cpp [new file with mode: 0644]
src/taglib/fileref.h [new file with mode: 0644]
src/taglib/flac/flacfile.cpp [new file with mode: 0644]
src/taglib/flac/flacfile.h [new file with mode: 0644]
src/taglib/flac/flacproperties.cpp [new file with mode: 0644]
src/taglib/flac/flacproperties.h [new file with mode: 0644]
src/taglib/mp4/mp4atom.cpp [new file with mode: 0644]
src/taglib/mp4/mp4atom.h [new file with mode: 0644]
src/taglib/mp4/mp4coverart.cpp [new file with mode: 0644]
src/taglib/mp4/mp4coverart.h [new file with mode: 0644]
src/taglib/mp4/mp4file.cpp [new file with mode: 0644]
src/taglib/mp4/mp4file.h [new file with mode: 0644]
src/taglib/mp4/mp4item.cpp [new file with mode: 0644]
src/taglib/mp4/mp4item.h [new file with mode: 0644]
src/taglib/mp4/mp4properties.cpp [new file with mode: 0644]
src/taglib/mp4/mp4properties.h [new file with mode: 0644]
src/taglib/mp4/mp4tag.cpp [new file with mode: 0644]
src/taglib/mp4/mp4tag.h [new file with mode: 0644]
src/taglib/mpc/mpcfile.cpp [new file with mode: 0644]
src/taglib/mpc/mpcfile.h [new file with mode: 0644]
src/taglib/mpc/mpcproperties.cpp [new file with mode: 0644]
src/taglib/mpc/mpcproperties.h [new file with mode: 0644]
src/taglib/mpeg/id3v1/id3v1genres.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v1/id3v1genres.h [new file with mode: 0644]
src/taglib/mpeg/id3v1/id3v1tag.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v1/id3v1tag.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/attachedpictureframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/commentsframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/commentsframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/popularimeterframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/popularimeterframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/privateframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/privateframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/relativevolumeframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/textidentificationframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/textidentificationframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/unknownframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/unknownframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/urllinkframe.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/frames/urllinkframe.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2.2.0.txt [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2.3.0.txt [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2.4.0-frames.txt [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2.4.0-structure.txt [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2extendedheader.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2extendedheader.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2footer.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2footer.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2frame.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2frame.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2framefactory.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2framefactory.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2header.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2header.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2synchdata.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2synchdata.h [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2tag.cpp [new file with mode: 0644]
src/taglib/mpeg/id3v2/id3v2tag.h [new file with mode: 0644]
src/taglib/mpeg/mpegfile.cpp [new file with mode: 0644]
src/taglib/mpeg/mpegfile.h [new file with mode: 0644]
src/taglib/mpeg/mpegheader.cpp [new file with mode: 0644]
src/taglib/mpeg/mpegheader.h [new file with mode: 0644]
src/taglib/mpeg/mpegproperties.cpp [new file with mode: 0644]
src/taglib/mpeg/mpegproperties.h [new file with mode: 0644]
src/taglib/mpeg/xingheader.cpp [new file with mode: 0644]
src/taglib/mpeg/xingheader.h [new file with mode: 0644]
src/taglib/ogg/flac/oggflacfile.cpp [new file with mode: 0644]
src/taglib/ogg/flac/oggflacfile.h [new file with mode: 0644]
src/taglib/ogg/oggfile.cpp [new file with mode: 0644]
src/taglib/ogg/oggfile.h [new file with mode: 0644]
src/taglib/ogg/oggpage.cpp [new file with mode: 0644]
src/taglib/ogg/oggpage.h [new file with mode: 0644]
src/taglib/ogg/oggpageheader.cpp [new file with mode: 0644]
src/taglib/ogg/oggpageheader.h [new file with mode: 0644]
src/taglib/ogg/speex/speexfile.cpp [new file with mode: 0644]
src/taglib/ogg/speex/speexfile.h [new file with mode: 0644]
src/taglib/ogg/speex/speexproperties.cpp [new file with mode: 0644]
src/taglib/ogg/speex/speexproperties.h [new file with mode: 0644]
src/taglib/ogg/vorbis/vorbisfile.cpp [new file with mode: 0644]
src/taglib/ogg/vorbis/vorbisfile.h [new file with mode: 0644]
src/taglib/ogg/vorbis/vorbisproperties.cpp [new file with mode: 0644]
src/taglib/ogg/vorbis/vorbisproperties.h [new file with mode: 0644]
src/taglib/ogg/xiphcomment.cpp [new file with mode: 0644]
src/taglib/ogg/xiphcomment.h [new file with mode: 0644]
src/taglib/riff/aiff/aifffile.cpp [new file with mode: 0644]
src/taglib/riff/aiff/aifffile.h [new file with mode: 0644]
src/taglib/riff/aiff/aiffproperties.cpp [new file with mode: 0644]
src/taglib/riff/aiff/aiffproperties.h [new file with mode: 0644]
src/taglib/riff/rifffile.cpp [new file with mode: 0644]
src/taglib/riff/rifffile.h [new file with mode: 0644]
src/taglib/riff/wav/wavfile.cpp [new file with mode: 0644]
src/taglib/riff/wav/wavfile.h [new file with mode: 0644]
src/taglib/riff/wav/wavproperties.cpp [new file with mode: 0644]
src/taglib/riff/wav/wavproperties.h [new file with mode: 0644]
src/taglib/tag.cpp [new file with mode: 0644]
src/taglib/tag.h [new file with mode: 0644]
src/taglib/taglib_config.h.in [new file with mode: 0644]
src/taglib/taglib_export.h [new file with mode: 0644]
src/taglib/tagunion.cpp [new file with mode: 0644]
src/taglib/tagunion.h [new file with mode: 0644]
src/taglib/toolkit/taglib.h [new file with mode: 0644]
src/taglib/toolkit/tbytevector.cpp [new file with mode: 0644]
src/taglib/toolkit/tbytevector.h [new file with mode: 0644]
src/taglib/toolkit/tbytevectorlist.cpp [new file with mode: 0644]
src/taglib/toolkit/tbytevectorlist.h [new file with mode: 0644]
src/taglib/toolkit/tdebug.cpp [new file with mode: 0644]
src/taglib/toolkit/tdebug.h [new file with mode: 0644]
src/taglib/toolkit/tfile.cpp [new file with mode: 0644]
src/taglib/toolkit/tfile.h [new file with mode: 0644]
src/taglib/toolkit/tlist.h [new file with mode: 0644]
src/taglib/toolkit/tlist.tcc [new file with mode: 0644]
src/taglib/toolkit/tmap.h [new file with mode: 0644]
src/taglib/toolkit/tmap.tcc [new file with mode: 0644]
src/taglib/toolkit/tstring.cpp [new file with mode: 0644]
src/taglib/toolkit/tstring.h [new file with mode: 0644]
src/taglib/toolkit/tstringlist.cpp [new file with mode: 0644]
src/taglib/toolkit/tstringlist.h [new file with mode: 0644]
src/taglib/toolkit/unicode.cpp [new file with mode: 0644]
src/taglib/toolkit/unicode.h [new file with mode: 0644]
src/taglib/trueaudio/trueaudiofile.cpp [new file with mode: 0644]
src/taglib/trueaudio/trueaudiofile.h [new file with mode: 0644]
src/taglib/trueaudio/trueaudioproperties.cpp [new file with mode: 0644]
src/taglib/trueaudio/trueaudioproperties.h [new file with mode: 0644]
src/taglib/wavpack/wavpackfile.cpp [new file with mode: 0644]
src/taglib/wavpack/wavpackfile.h [new file with mode: 0644]
src/taglib/wavpack/wavpackproperties.cpp [new file with mode: 0644]
src/taglib/wavpack/wavpackproperties.h [new file with mode: 0644]
src/tagresolver.cpp
src/tagresolver.h
src/ui/busywidget.ui

diff --git a/AUTHORS.TagLib b/AUTHORS.TagLib
new file mode 100644 (file)
index 0000000..8872bd8
--- /dev/null
@@ -0,0 +1,11 @@
+Scott Wheeler <wheeler@kde.org>
+ Author, maintainer
+Ismael Orenstein <orenstein@kde.org>
+ Xing header implementation
+Allan Sandfeld Jensen <kde@carewolf.org>
+ FLAC metadata implementation
+Teemu Tervo <teemu.tervo@gmx.net>
+ Numerous bug reports and fixes
+
+Please send all patches and questions to taglib-devel@kde.org rather than to
+individual developers!
diff --git a/COPYING.LGPL b/COPYING.LGPL
new file mode 100644 (file)
index 0000000..e38ffa8
--- /dev/null
@@ -0,0 +1,481 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                    59 Temple Place - Suite 330
+                    Boston, MA 02111-1307, USA.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+          How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
index ead97fc..31dcd75 100644 (file)
Binary files a/resources/someplayer.png and b/resources/someplayer.png differ
index 0f501be..1269327 100644 (file)
@@ -9,6 +9,25 @@ QT       += core gui phonon sql
 TARGET = someplayer
 TEMPLATE = app
 
+INCLUDEPATH += src/taglib
+INCLUDEPATH += src/taglib/toolkit
+INCLUDEPATH += src/taglib/ape
+INCLUDEPATH += src/taglib/asf
+INCLUDEPATH += src/taglib/flac
+INCLUDEPATH += src/taglib/mp4
+INCLUDEPATH += src/taglib/mpc
+INCLUDEPATH += src/taglib/mpeg
+INCLUDEPATH += src/taglib/mpeg/id3v1
+INCLUDEPATH += src/taglib/mpeg/id3v2
+INCLUDEPATH += src/taglib/ogg
+INCLUDEPATH += src/taglib/ogg/flac
+INCLUDEPATH += src/taglib/ogg/speex
+INCLUDEPATH += src/taglib/ogg/vorbis
+INCLUDEPATH += src/taglib/riff
+INCLUDEPATH += src/taglib/riff/aiff
+INCLUDEPATH += src/taglib/riff/wav
+INCLUDEPATH += src/taglib/trueaudio
+INCLUDEPATH += src/taglib/wavpack
 
 SOURCES += src/main.cpp\
         src/mainwindow.cpp \
@@ -26,7 +45,77 @@ SOURCES += src/main.cpp\
     src/busywidget.cpp \
     src/trackrenderer.cpp \
     src/config.cpp \
-    src/playlistdialog.cpp
+    src/playlistdialog.cpp \
+    src/taglib/tagunion.cpp \
+    src/taglib/tag.cpp \
+    src/taglib/fileref.cpp \
+    src/taglib/audioproperties.cpp \
+    src/taglib/ape/apetag.cpp \
+    src/taglib/ape/apeitem.cpp \
+    src/taglib/ape/apefooter.cpp \
+    src/taglib/asf/asftag.cpp \
+    src/taglib/asf/asfproperties.cpp \
+    src/taglib/asf/asffile.cpp \
+    src/taglib/asf/asfattribute.cpp \
+    src/taglib/flac/flacproperties.cpp \
+    src/taglib/flac/flacfile.cpp \
+    src/taglib/mp4/mp4tag.cpp \
+    src/taglib/mp4/mp4properties.cpp \
+    src/taglib/mp4/mp4item.cpp \
+    src/taglib/mp4/mp4file.cpp \
+    src/taglib/mp4/mp4coverart.cpp \
+    src/taglib/mp4/mp4atom.cpp \
+    src/taglib/mpc/mpcproperties.cpp \
+    src/taglib/mpc/mpcfile.cpp \
+    src/taglib/mpeg/xingheader.cpp \
+    src/taglib/mpeg/mpegproperties.cpp \
+    src/taglib/mpeg/mpegheader.cpp \
+    src/taglib/mpeg/mpegfile.cpp \
+    src/taglib/mpeg/id3v1/id3v1tag.cpp \
+    src/taglib/mpeg/id3v1/id3v1genres.cpp \
+    src/taglib/mpeg/id3v2/id3v2tag.cpp \
+    src/taglib/mpeg/id3v2/id3v2synchdata.cpp \
+    src/taglib/mpeg/id3v2/id3v2header.cpp \
+    src/taglib/mpeg/id3v2/id3v2framefactory.cpp \
+    src/taglib/mpeg/id3v2/id3v2frame.cpp \
+    src/taglib/mpeg/id3v2/id3v2footer.cpp \
+    src/taglib/mpeg/id3v2/id3v2extendedheader.cpp \
+    src/taglib/mpeg/id3v2/frames/urllinkframe.cpp \
+    src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp \
+    src/taglib/mpeg/id3v2/frames/unknownframe.cpp \
+    src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp \
+    src/taglib/mpeg/id3v2/frames/textidentificationframe.cpp \
+    src/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp \
+    src/taglib/mpeg/id3v2/frames/privateframe.cpp \
+    src/taglib/mpeg/id3v2/frames/popularimeterframe.cpp \
+    src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp \
+    src/taglib/mpeg/id3v2/frames/commentsframe.cpp \
+    src/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp \
+    src/taglib/ogg/xiphcomment.cpp \
+    src/taglib/ogg/oggpageheader.cpp \
+    src/taglib/ogg/oggpage.cpp \
+    src/taglib/ogg/oggfile.cpp \
+    src/taglib/ogg/flac/oggflacfile.cpp \
+    src/taglib/ogg/speex/speexproperties.cpp \
+    src/taglib/ogg/speex/speexfile.cpp \
+    src/taglib/ogg/vorbis/vorbisproperties.cpp \
+    src/taglib/ogg/vorbis/vorbisfile.cpp \
+    src/taglib/riff/rifffile.cpp \
+    src/taglib/riff/aiff/aiffproperties.cpp \
+    src/taglib/riff/aiff/aifffile.cpp \
+    src/taglib/riff/wav/wavproperties.cpp \
+    src/taglib/riff/wav/wavfile.cpp \
+    src/taglib/toolkit/unicode.cpp \
+    src/taglib/toolkit/tstringlist.cpp \
+    src/taglib/toolkit/tstring.cpp \
+    src/taglib/toolkit/tfile.cpp \
+    src/taglib/toolkit/tdebug.cpp \
+    src/taglib/toolkit/tbytevectorlist.cpp \
+    src/taglib/toolkit/tbytevector.cpp \
+    src/taglib/trueaudio/trueaudioproperties.cpp \
+    src/taglib/trueaudio/trueaudiofile.cpp \
+    src/taglib/wavpack/wavpackproperties.cpp \
+    src/taglib/wavpack/wavpackfile.cpp
 
 HEADERS  += src/mainwindow.h \
                src/player/player.h \
@@ -45,7 +134,81 @@ HEADERS  += src/mainwindow.h \
     src/busywidget.h \
     src/trackrenderer.h \
     src/config.h \
-    src/playlistdialog.h
+    src/playlistdialog.h \
+    src/taglib/taglib_export.h \
+    src/taglib/tagunion.h \
+    src/taglib/tag.h \
+    src/taglib/fileref.h \
+    src/taglib/audioproperties.h \
+    src/taglib/ape/apetag.h \
+    src/taglib/ape/apeitem.h \
+    src/taglib/ape/apefooter.h \
+    src/taglib/asf/asftag.h \
+    src/taglib/asf/asfproperties.h \
+    src/taglib/asf/asffile.h \
+    src/taglib/asf/asfattribute.h \
+    src/taglib/flac/flacproperties.h \
+    src/taglib/flac/flacfile.h \
+    src/taglib/mp4/mp4tag.h \
+    src/taglib/mp4/mp4properties.h \
+    src/taglib/mp4/mp4item.h \
+    src/taglib/mp4/mp4file.h \
+    src/taglib/mp4/mp4coverart.h \
+    src/taglib/mp4/mp4atom.h \
+    src/taglib/mpc/mpcproperties.h \
+    src/taglib/mpc/mpcfile.h \
+    src/taglib/mpeg/xingheader.h \
+    src/taglib/mpeg/mpegproperties.h \
+    src/taglib/mpeg/mpegheader.h \
+    src/taglib/mpeg/mpegfile.h \
+    src/taglib/mpeg/id3v1/id3v1tag.h \
+    src/taglib/mpeg/id3v1/id3v1genres.h \
+    src/taglib/mpeg/id3v2/id3v2tag.h \
+    src/taglib/mpeg/id3v2/id3v2synchdata.h \
+    src/taglib/mpeg/id3v2/id3v2header.h \
+    src/taglib/mpeg/id3v2/id3v2framefactory.h \
+    src/taglib/mpeg/id3v2/id3v2frame.h \
+    src/taglib/mpeg/id3v2/id3v2footer.h \
+    src/taglib/mpeg/id3v2/id3v2extendedheader.h \
+    src/taglib/mpeg/id3v2/frames/urllinkframe.h \
+    src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h \
+    src/taglib/mpeg/id3v2/frames/unknownframe.h \
+    src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h \
+    src/taglib/mpeg/id3v2/frames/textidentificationframe.h \
+    src/taglib/mpeg/id3v2/frames/relativevolumeframe.h \
+    src/taglib/mpeg/id3v2/frames/privateframe.h \
+    src/taglib/mpeg/id3v2/frames/popularimeterframe.h \
+    src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h \
+    src/taglib/mpeg/id3v2/frames/commentsframe.h \
+    src/taglib/mpeg/id3v2/frames/attachedpictureframe.h \
+    src/taglib/ogg/xiphcomment.h \
+    src/taglib/ogg/oggpageheader.h \
+    src/taglib/ogg/oggpage.h \
+    src/taglib/ogg/oggfile.h \
+    src/taglib/ogg/flac/oggflacfile.h \
+    src/taglib/ogg/speex/speexproperties.h \
+    src/taglib/ogg/speex/speexfile.h \
+    src/taglib/ogg/vorbis/vorbisproperties.h \
+    src/taglib/ogg/vorbis/vorbisfile.h \
+    src/taglib/riff/rifffile.h \
+    src/taglib/riff/aiff/aiffproperties.h \
+    src/taglib/riff/aiff/aifffile.h \
+    src/taglib/riff/wav/wavproperties.h \
+    src/taglib/riff/wav/wavfile.h \
+    src/taglib/toolkit/unicode.h \
+    src/taglib/toolkit/tstringlist.h \
+    src/taglib/toolkit/tstring.h \
+    src/taglib/toolkit/tmap.h \
+    src/taglib/toolkit/tlist.h \
+    src/taglib/toolkit/tfile.h \
+    src/taglib/toolkit/tdebug.h \
+    src/taglib/toolkit/tbytevectorlist.h \
+    src/taglib/toolkit/tbytevector.h \
+    src/taglib/toolkit/taglib.h \
+    src/taglib/trueaudio/trueaudioproperties.h \
+    src/taglib/trueaudio/trueaudiofile.h \
+    src/taglib/wavpack/wavpackproperties.h \
+    src/taglib/wavpack/wavpackfile.h
 
 FORMS    += src/ui/mainwindow.ui \
     src/ui/playerform.ui \
@@ -69,4 +232,14 @@ RESOURCES += \
 OTHER_FILES += \
     README \
     COPYING \
-    resources/someplayer.desktop
+    resources/someplayer.desktop \
+    src/taglib/taglib_config.h.in \
+    src/taglib/ape/ape-tag-format.txt \
+    src/taglib/mpeg/id3v2/id3v2.4.0-structure.txt \
+    src/taglib/mpeg/id3v2/id3v2.4.0-frames.txt \
+    src/taglib/mpeg/id3v2/id3v2.3.0.txt \
+    src/taglib/mpeg/id3v2/id3v2.2.0.txt \
+    src/taglib/toolkit/tmap.tcc \
+    src/taglib/toolkit/tlist.tcc \
+    AUTHORS.TagLib \
+    COPYING.LGPL
index ace5a7d..c615376 100644 (file)
 
 #include "busywidget.h"
 #include "ui_busywidget.h"
+#include <QDebug>
 
 BusyWidget::BusyWidget(QWidget *parent) :
     QWidget(parent),
     ui(new Ui::BusyWidget)
 {
     ui->setupUi(this);
+    ui->progressBar->setValue(0);
 }
 
 BusyWidget::~BusyWidget()
@@ -35,3 +37,12 @@ BusyWidget::~BusyWidget()
 void BusyWidget::setText(QString text) {
        ui->label->setText(text);
 }
+
+void BusyWidget::setMax(int max) {
+       ui->progressBar->setMaximum(max);
+       ui->progressBar->setValue(0);
+}
+
+void BusyWidget::tick() {
+       ui->progressBar->setValue(ui->progressBar->value()+1);
+}
index 9439f8d..86ab387 100644 (file)
 #include <QWidget>
 
 namespace Ui {
-    class BusyWidget;
+       class BusyWidget;
 }
 
 class BusyWidget : public QWidget
 {
-    Q_OBJECT
+       Q_OBJECT
 
 public:
-    explicit BusyWidget(QWidget *parent = 0);
-    ~BusyWidget();
+       explicit BusyWidget(QWidget *parent = 0);
+       ~BusyWidget();
        void setText(QString text);
+public slots:
+       void setMax(int);
+       void tick();
 
 private:
-    Ui::BusyWidget *ui;
+       Ui::BusyWidget *ui;
 };
 
 #endif // BUSYWIDGET_H
index 58d7feb..562afd5 100644 (file)
@@ -30,9 +30,9 @@ Library::Library(QString databasePath, QString playlistsPath) : QObject(0) {
        _playlist_storage = new FileStorage(playlistsPath);
        _scanner = new MediaScanner();
        _resolver = new TagResolver(this);
-       connect(_scanner, SIGNAL(scanFinish(QStringList)), _resolver, SLOT(decode(QStringList)));
+       connect(_scanner, SIGNAL(scanFinish(QStringList)), this, SLOT(_scanned(QStringList)));
        connect(_resolver, SIGNAL(done()), this, SIGNAL(done()));
-       connect(_resolver, SIGNAL(decoded(Track)), this, SLOT(addTrack(Track)));
+       connect(_resolver, SIGNAL(decoded(Track)), this, SLOT(_decoded(Track)));
 }
 
 Library::~Library() {
@@ -132,3 +132,13 @@ Playlist Library::getCurrentPlaylist() {
 void Library::saveCurrentPlaylist(const Playlist &playlist) {
        _playlist_storage->saveCurrentPlaylist(playlist);
 }
+
+void Library::_decoded(Track track) {
+       emit trackAdded();
+       addTrack(track);
+}
+
+void Library::_scanned(QStringList files) {
+       emit addingTracks(files.count());
+       _resolver->decode(files);
+}
index b7e674b..9268609 100644 (file)
@@ -72,6 +72,8 @@ namespace SomePlayer {
                signals:
                        void done();
                        void busy(QString);
+                       void addingTracks(int);
+                       void trackAdded();
 
                private:
                        DbStorage *_library_storage;
@@ -79,6 +81,10 @@ namespace SomePlayer {
                        MediaScanner *_scanner;
                        TagResolver *_resolver;
 
+               private slots:
+                       void _scanned(QStringList);
+                       void _decoded(Track);
+
                public slots:
                        void removeTrack(Track);
                        void addTrack(Track);
index a7d59d0..424023f 100644 (file)
@@ -58,6 +58,8 @@ MainWindow::MainWindow(QWidget *parent) :
        connect(clear_playlist, SIGNAL(triggered()), this, SLOT(_clear_current_playlist()));
        connect(add_files, SIGNAL(triggered()), this, SLOT(_add_files()));
        connect(_library, SIGNAL(done()), this, SLOT(library()));
+       connect(_library, SIGNAL(addingTracks(int)), _busy_widget, SLOT(setMax(int)));
+       connect(_library, SIGNAL(trackAdded()), _busy_widget, SLOT(tick()));
        connect(_library_form, SIGNAL(done()), this, SLOT(library()));
        connect(_library_form, SIGNAL(busy(QString)), this, SLOT(showBusyWidget(QString)));
        connect(ui->searchButton, SIGNAL(clicked()), this, SLOT(_toggle_search_line()));
index c026fa6..1b84640 100644 (file)
@@ -27,7 +27,7 @@ using namespace SomePlayer::Storage;
 MediaScanner::MediaScanner(QObject *parent) :
                QThread(parent), _stopped(false), _initialized(false)
 {
-       REGISTERED_FILE_EXTENSIONS << "mp3" << "flac" << "wma" << "acc";
+       REGISTERED_FILE_EXTENSIONS << "mp3" << "flac" << "wma" << "acc" << "ogg";
 }
 
 void MediaScanner::run() {
index 6ecab29..2008b4b 100644 (file)
@@ -47,5 +47,6 @@ namespace SomePlayer {
 #include "config.h"
 
 #define _DYNAMIC_PLAYLIST_MAX_COUNT_ 50
+#define NDEBUG
 
 #endif
diff --git a/src/taglib/ape/ape-tag-format.txt b/src/taglib/ape/ape-tag-format.txt
new file mode 100644 (file)
index 0000000..21ff1c8
--- /dev/null
@@ -0,0 +1,170 @@
+================================================================================
+= APE Tag Specification, Version 2.000
+================================================================================
+
+Original Content     (C) 2004, Frank Klemm <frank.klemm@elster.offl.uni-jena.de>
+Formatting / Editing (C) 2004, Scott Wheeler <wheeler@kde.org>
+
+================================================================================
+= Contents
+================================================================================
+
+1   - APE Tag General Structure
+2   - APE Tag Header / Footer Format
+3   - APE Tag Flags
+4   - APE Tag Item Format
+5   - APE Tag Item Supported Keys
+6   - APE Tag Item Content
+7   - Data Types
+7.1 - Data Types / UTF-8
+7.2 - Data Types / Dates
+7.3 - Data Types / Timestamps
+
+================================================================================
+= 1 - APE Tag General Structure
+================================================================================
+
+Member of Basic Components of SV8 Stream Note:
+
+It is strongly recommended that the data size be stored in the tags.  The size
+should normally be in the roughly one kilobyte, never more than 8 kilobytes.
+
+Larger data should be stored externally using link entries.  Linked data is much
+easier to process by normal programs, so for instance JPEG data should not be
+included inside the audio file.
+
+APE Tag Version 2.000 (with header, recommended):
+
+/================================\
+| APE Tag Header    | 32 bytes   |
+|-------------------|------------|
+| APE Tag Item 1    | > 10 bytes |
+| APE Tag Item 2    | > 10 bytes |
+| APE Tag Item n-1  | > 10 bytes |
+| APE Tag Item n    | > 10 bytes |
+|-------------------|------------|
+| APE Tag Footer    | 32 bytes   |
+\================================/
+
+
+APE tag items should be sorted ascending by size.  When streaming, parts of the
+APE tag may be dropped to reduce the danger of drop outs between tracks.  This
+is not required, but is strongly recommended.  It would be desirable for the i
+tems to be sorted by importance / size, but this is not feasible.  This
+convention should only be broken when adding less important small items and it
+is not desirable to rewrite the entire tag.  An APE tag at the end of a file
+(the recommended location) must have at least a footer; an APE tag at the
+beginning of a file (strongly discouraged) must have at least a header.
+
+APE Tag Version 1.000 (without header, deprecated)
+
+/================================\
+| APE Tag Item 1    | > 10 bytes |
+| APE Tag Item 2    | > 10 bytes |
+| APE Tag Item n-1  | > 10 bytes |
+| APE Tag Item n    | > 10 bytes |
+|-------------------|------------|
+| APE Tag Footer    | 32 bytes   |
+\================================/
+
+================================================================================
+= 2 - APE Tag Header / Footer Format
+================================================================================
+
+Contains number, length and attributes of all tag items
+
+Header and Footer are different in 1 bit in the Tags Flags to distinguish
+between them.
+
+Member of APE Tag 2.0
+
+/===========================================================================\
+| Preamble       | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' }     |
+|----------------|---------|------------------------------------------------|
+| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000     |
+|----------------|---------|------------------------------------------------|
+| Tag Size       | 4 bytes | Tag size in bytes including footer and all tag |
+|                |         | items excluding the header (for 1.000          |
+|                |         | compatibility)                                 |
+|----------------|---------|------------------------------------------------|
+| Item Count     | 4 bytes | Number of items in the tag                     |
+|----------------|---------|------------------------------------------------|
+| Tag Flags      | 4 bytes | Global flags                                   |
+|----------------|---------|------------------------------------------------|
+| Reserved       | 8 bytes | Must be zeroed                                 |
+\===========================================================================/
+
+================================================================================
+= 3 - APE Tag Flags
+================================================================================
+
+The general flag structure for either items or headers / footers is the same.
+Bits 31, 30 and 29 are specific to headers / footers, whereas 2 through 0 are
+item specific.
+
+Note: APE Tags from Version 1.0 do not use any of the following.  All flags in
+that version are zeroed and ignored when reading.
+
+/=================================================================\
+| Contains Header | Bit 31      | 1 - has header | 0 - no header  |
+|-----------------|-------------|---------------------------------|
+| Contains Footer | Bit 30      | 1 - has footer | 0 - no footer  |
+|-----------------|-------------|---------------------------------|
+| Is Header       | Bit 29      | 1 - is header  | 0 - is footer  |
+|-----------------|-------------|---------------------------------|
+| Undefined       | Bits 28 - 3 | Undefined, must be zeroed       |
+|-----------------|-------------|---------------------------------|
+| Encoding        | Bits 2 - 1  | 00 - UTF-8                      |
+|                 |             | 01 - Binary Data *              |
+|                 |             | 10 - External Reference **      |
+|                 |             | 11 - Reserved                   |
+|-----------------|-------------|---------------------------------|
+| Read Only       | Bit 0       | 1 - read only  | 0 - read/write |
+\=================================================================/
+
+ (*) Should be ignored by tools for editing text values
+(**) Allowed external reference formats:
+     - http://host/directory/filename.ext
+     - ftp://host/directory/filename.ext
+     - filename.ext
+     - /directory/filename.ext
+     - DRIVE:/directory/filename.ext
+
+     Note: External references are also UTF-8 encoded.
+
+================================================================================
+= 4 - APE Tag Item Format
+================================================================================
+
+APE Tag Items are stored as key-value pairs.  APE Tags Item Key are case
+sensitive, however it is illegal to use keys which only differ in case and
+it is recommended that tag reading not be case sensitive.
+
+Every key can only occur (at most) once. It is not possible to repeat a key
+to signify updated contents.
+
+Tags can be partially or completely repeated in the streaming format.  This
+makes it possible to display an artist and / or title if it was missed at the
+beginning of the stream.  It is recommended that the important information like
+artist, album and title should occur approximately every 2 minutes in the
+stream and again 5 to 10 seconds before the end.  However, care should be tak
+en not to replicate this information too often or during passages with high
+bitrate demands to avoid unnecessary drop-outs.
+
+/==============================================================================\
+| Content Size   | 4 bytes       | Length of the value in bytes                |
+|----------------|---------------|---------------------------------------------|
+| Flags          | 4 bytes       | Item flags                                  |
+|----------------|---------------|---------------------------------------------|
+| Key            | 2 - 255 bytes | Item key                                    |
+|----------------|---------------|---------------------------------------------|
+| Key Terminator | 1 byte        | Null byte that indicates the end of the key |
+|----------------|---------------|---------------------------------------------|
+| Value          | variable      | Content (formatted according to the flags)  |
+\==============================================================================/
+
+================================================================================
+
+Sections 5 - 7 haven't yet been converted from:
+
+http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
diff --git a/src/taglib/ape/apefooter.cpp b/src/taglib/ape/apefooter.cpp
new file mode 100644 (file)
index 0000000..da6494b
--- /dev/null
@@ -0,0 +1,236 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+                           (C) 2002 - 2008 by Scott Wheeler (id3v2header.cpp)
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <iostream>
+#include <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "apefooter.h"
+
+using namespace TagLib;
+using namespace APE;
+
+class Footer::FooterPrivate
+{
+public:
+  FooterPrivate() : version(0),
+                    footerPresent(true),
+                    headerPresent(false),
+                    isHeader(false),
+                    itemCount(0),
+                    tagSize(0) {}
+
+  ~FooterPrivate() {}
+
+  uint version;
+
+  bool footerPresent;
+  bool headerPresent;
+
+  bool isHeader;
+
+  uint itemCount;
+  uint tagSize;
+
+  static const uint size = 32;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Footer::size()
+{
+  return FooterPrivate::size;
+}
+
+ByteVector Footer::fileIdentifier()
+{
+  return ByteVector::fromCString("APETAGEX");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Footer::Footer()
+{
+  d = new FooterPrivate;
+}
+
+Footer::Footer(const ByteVector &data)
+{
+  d = new FooterPrivate;
+  parse(data);
+}
+
+Footer::~Footer()
+{
+  delete d;
+}
+
+TagLib::uint Footer::version() const
+{
+  return d->version;
+}
+
+bool Footer::headerPresent() const
+{
+  return d->headerPresent;
+}
+
+bool Footer::footerPresent() const
+{
+  return d->footerPresent;
+}
+
+bool Footer::isHeader() const
+{
+  return d->isHeader;
+}
+
+void Footer::setHeaderPresent(bool b) const
+{
+  d->headerPresent = b;
+}
+
+TagLib::uint Footer::itemCount() const
+{
+  return d->itemCount;
+}
+
+void Footer::setItemCount(uint s)
+{
+  d->itemCount = s;
+}
+
+TagLib::uint Footer::tagSize() const
+{
+  return d->tagSize;
+}
+
+TagLib::uint Footer::completeTagSize() const
+{
+  if(d->headerPresent)
+    return d->tagSize + d->size;
+  else
+    return d->tagSize;
+}
+
+void Footer::setTagSize(uint s)
+{
+  d->tagSize = s;
+}
+
+void Footer::setData(const ByteVector &data)
+{
+  parse(data);
+}
+
+ByteVector Footer::renderFooter() const
+{
+    return render(false);
+}
+
+ByteVector Footer::renderHeader() const
+{
+    if (!d->headerPresent) return ByteVector();
+
+    return render(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void Footer::parse(const ByteVector &data)
+{
+  if(data.size() < size())
+    return;
+
+  // The first eight bytes, data[0..7], are the File Identifier, "APETAGEX".
+
+  // Read the version number
+
+  d->version = data.mid(8, 4).toUInt(false);
+
+  // Read the tag size
+
+  d->tagSize = data.mid(12, 4).toUInt(false);
+
+  // Read the item count
+
+  d->itemCount = data.mid(16, 4).toUInt(false);
+
+  // Read the flags
+
+  std::bitset<32> flags(data.mid(20, 4).toUInt(false));
+
+  d->headerPresent = flags[31];
+  d->footerPresent = !flags[30];
+  d->isHeader = flags[29];
+
+}
+
+ByteVector Footer::render(bool isHeader) const
+{
+  ByteVector v;
+
+  // add the file identifier -- "APETAGEX"
+
+  v.append(fileIdentifier());
+
+  // add the version number -- we always render a 2.000 tag regardless of what
+  // the tag originally was.
+
+  v.append(ByteVector::fromUInt(2000, false));
+
+  // add the tag size
+
+  v.append(ByteVector::fromUInt(d->tagSize, false));
+
+  // add the item count
+
+  v.append(ByteVector::fromUInt(d->itemCount, false));
+
+  // render and add the flags
+
+  std::bitset<32> flags;
+
+  flags[31] = d->headerPresent;
+  flags[30] = false; // footer is always present
+  flags[29] = isHeader;
+
+  v.append(ByteVector::fromUInt(flags.to_ulong(), false));
+
+  // add the reserved 64bit
+
+  v.append(ByteVector::fromLongLong(0));
+
+  return v;
+}
diff --git a/src/taglib/ape/apefooter.h b/src/taglib/ape/apefooter.h
new file mode 100644 (file)
index 0000000..f0d921f
--- /dev/null
@@ -0,0 +1,173 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_APEFOOTER_H
+#define TAGLIB_APEFOOTER_H
+
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace APE {
+
+    //! An implementation of APE footers
+
+    /*!
+     * This class implements APE footers (and headers). It attempts to follow, both
+     * semantically and programatically, the structure specified in
+     * the APE v2.0 standard.  The API is based on the properties of APE footer and
+     * headers specified there.
+     */
+
+    class TAGLIB_EXPORT Footer
+    {
+    public:
+      /*!
+       * Constructs an empty APE footer.
+       */
+      Footer();
+
+      /*!
+       * Constructs an APE footer based on \a data.  parse() is called
+       * immediately.
+       */
+      Footer(const ByteVector &data);
+
+      /*!
+       * Destroys the footer.
+       */
+      virtual ~Footer();
+
+      /*!
+       * Returns the version number.  (Note: This is the 1000 or 2000.)
+       */
+      uint version() const;
+
+      /*!
+       * Returns true if a header is present in the tag.
+       */
+      bool headerPresent() const;
+
+      /*!
+       * Returns true if a footer is present in the tag.
+       */
+      bool footerPresent() const;
+
+      /*!
+       * Returns true this is actually the header.
+       */
+      bool isHeader() const;
+
+      /*!
+       * Sets whether the header should be rendered or not
+       */
+      void setHeaderPresent(bool b) const;
+
+      /*!
+       * Returns the number of items in the tag.
+       */
+      uint itemCount() const;
+
+      /*!
+       * Set the item count to \a s.
+       * \see itemCount()
+       */
+      void setItemCount(uint s);
+
+      /*!
+       * Returns the tag size in bytes.  This is the size of the frame content and footer.
+       * The size of the \e entire tag will be this plus the header size, if present.
+       *
+       * \see completeTagSize()
+       */
+      uint tagSize() const;
+
+      /*!
+       * Returns the tag size, including if present, the header
+       * size.
+       *
+       * \see tagSize()
+       */
+      uint completeTagSize() const;
+
+      /*!
+       * Set the tag size to \a s.
+       * \see tagSize()
+       */
+      void setTagSize(uint s);
+
+      /*!
+       * Returns the size of the footer.  Presently this is always 32 bytes.
+       */
+      static uint size();
+
+      /*!
+       * Returns the string used to identify an APE tag inside of a file.
+       * Presently this is always "APETAGEX".
+       */
+      static ByteVector fileIdentifier();
+
+      /*!
+       * Sets the data that will be used as the footer.  32 bytes,
+       * starting from \a data will be used.
+       */
+      void setData(const ByteVector &data);
+
+      /*!
+       * Renders the footer back to binary format.
+       */
+      ByteVector renderFooter() const;
+
+      /*!
+       * Renders the header corresponding to the footer. If headerPresent is
+       * set to false, it returns an empty ByteVector.
+       */
+      ByteVector renderHeader() const;
+
+    protected:
+      /*!
+       * Called by setData() to parse the footer data.  It makes this information
+       * available through the public API.
+       */
+      void parse(const ByteVector &data);
+
+      /*!
+       * Called by renderFooter and renderHeader
+       */
+      ByteVector render(bool isHeader) const;
+
+    private:
+      Footer(const Footer &);
+      Footer &operator=(const Footer &);
+
+      class FooterPrivate;
+      FooterPrivate *d;
+    };
+
+  }
+}
+
+#endif
diff --git a/src/taglib/ape/apeitem.cpp b/src/taglib/ape/apeitem.cpp
new file mode 100644 (file)
index 0000000..77a9ca0
--- /dev/null
@@ -0,0 +1,230 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevectorlist.h>
+#include <tdebug.h>
+
+#include "apeitem.h"
+
+using namespace TagLib;
+using namespace APE;
+
+class APE::Item::ItemPrivate
+{
+public:
+  ItemPrivate() : type(Text), readOnly(false) {}
+
+  Item::ItemTypes type;
+  String key;
+  ByteVector value;
+  StringList text;
+  bool readOnly;
+};
+
+APE::Item::Item()
+{
+  d = new ItemPrivate;
+}
+
+APE::Item::Item(const String &key, const String &value)
+{
+  d = new ItemPrivate;
+  d->key = key;
+  d->text.append(value);
+}
+
+APE::Item::Item(const String &key, const StringList &values)
+{
+  d = new ItemPrivate;
+  d->key = key;
+  d->text = values;
+}
+
+APE::Item::Item(const Item &item)
+{
+  d = new ItemPrivate(*item.d);
+}
+
+APE::Item::~Item()
+{
+  delete d;
+}
+
+Item &APE::Item::operator=(const Item &item)
+{
+  delete d;
+  d = new ItemPrivate(*item.d);
+  return *this;
+}
+
+void APE::Item::setReadOnly(bool readOnly)
+{
+  d->readOnly = readOnly;
+}
+
+bool APE::Item::isReadOnly() const
+{
+  return d->readOnly;
+}
+
+void APE::Item::setType(APE::Item::ItemTypes val)
+{
+  d->type = val;
+}
+
+APE::Item::ItemTypes APE::Item::type() const
+{
+  return d->type;
+}
+
+String APE::Item::key() const
+{
+  return d->key;
+}
+
+ByteVector APE::Item::value() const
+{
+  // This seems incorrect as it won't be actually rendering the value to keep it
+  // up to date.
+
+  return d->value;
+}
+
+void APE::Item::setKey(const String &key)
+{
+    d->key = key;
+}
+
+void APE::Item::setValue(const String &value)
+{
+    d->text = value;
+}
+
+void APE::Item::setValues(const StringList &value)
+{
+    d->text = value;
+}
+
+void APE::Item::appendValue(const String &value)
+{
+    d->text.append(value);
+}
+
+void APE::Item::appendValues(const StringList &values)
+{
+    d->text.append(values);
+}
+
+int APE::Item::size() const
+{
+  return 8 + d->key.size() + 1 + d->value.size();
+}
+
+StringList APE::Item::toStringList() const
+{
+  return d->text;
+}
+
+StringList APE::Item::values() const
+{
+  return d->text;
+}
+
+String APE::Item::toString() const
+{
+  return isEmpty() ? String::null : d->text.front();
+}
+
+bool APE::Item::isEmpty() const
+{
+  switch(d->type) {
+    case Text:
+    case Binary:
+      if(d->text.isEmpty())
+        return true;
+      if(d->text.size() == 1 && d->text.front().isEmpty())
+        return true;
+      return false;
+    case Locator:
+      return d->value.isEmpty();
+    default:
+      return false;
+  }
+}
+
+void APE::Item::parse(const ByteVector &data)
+{
+  // 11 bytes is the minimum size for an APE item
+
+  if(data.size() < 11) {
+    debug("APE::Item::parse() -- no data in item");
+    return;
+  }
+
+  uint valueLength  = data.mid(0, 4).toUInt(false);
+  uint flags        = data.mid(4, 4).toUInt(false);
+
+  d->key = String(data.mid(8), String::UTF8);
+
+  d->value = data.mid(8 + d->key.size() + 1, valueLength);
+
+  setReadOnly(flags & 1);
+  setType(ItemTypes((flags >> 1) & 3));
+
+  if(int(d->type) < 2)
+    d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8);
+}
+
+ByteVector APE::Item::render() const
+{
+  ByteVector data;
+  TagLib::uint flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
+  ByteVector value;
+
+  if(isEmpty())
+    return data;
+
+  if(d->type == Text) {
+    StringList::ConstIterator it = d->text.begin();
+
+    value.append(it->data(String::UTF8));
+    it++;
+    for(; it != d->text.end(); ++it) {
+      value.append('\0');
+      value.append(it->data(String::UTF8));
+    }
+    d->value = value;
+  }
+  else
+    value.append(d->value);
+
+  data.append(ByteVector::fromUInt(value.size(), false));
+  data.append(ByteVector::fromUInt(flags, false));
+  data.append(d->key.data(String::UTF8));
+  data.append(ByteVector('\0'));
+  data.append(value);
+
+  return data;
+}
diff --git a/src/taglib/ape/apeitem.h b/src/taglib/ape/apeitem.h
new file mode 100644 (file)
index 0000000..8558bb3
--- /dev/null
@@ -0,0 +1,204 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_APEITEM_H
+#define TAGLIB_APEITEM_H
+
+#include "tbytevector.h"
+#include "tstring.h"
+#include "tstringlist.h"
+
+namespace TagLib {
+
+  namespace APE {
+
+    //! An implementation of APE-items
+
+    /*!
+     * This class provides the features of items in the APEv2 standard.
+     */
+    class TAGLIB_EXPORT Item
+    {
+    public:
+      /*!
+       * Enum of types an Item can have. The value of 3 is reserved.
+       */
+      enum ItemTypes {
+        //! Item contains text information coded in UTF-8
+        Text = 0,
+        //! Item contains binary information
+        Binary = 1,
+        //! Item is a locator of external stored information
+        Locator = 2
+      };
+      /*!
+       * Constructs an empty item.
+       */
+      Item();
+
+      /*!
+       * Constructs an item with \a key and \a value.
+       */
+      // BIC: Remove this, StringList has a constructor from a single string
+      Item(const String &key, const String &value);
+
+      /*!
+       * Constructs an item with \a key and \a values.
+       */
+      Item(const String &key, const StringList &values);
+
+      /*!
+       * Construct an item as a copy of \a item.
+       */
+      Item(const Item &item);
+
+      /*!
+       * Destroys the item.
+       */
+      virtual ~Item();
+
+      /*!
+       * Copies the contents of \a item into this item.
+       */
+      Item &operator=(const Item &item);
+
+      /*!
+       * Returns the key.
+       */
+      String key() const;
+
+      /*!
+       * Returns the binary value.
+       *
+       * \deprecated This will be removed in the next binary incompatible version
+       * as it is not kept in sync with the things that are set using setValue()
+       * and friends.
+       */
+      ByteVector value() const;
+
+      /*!
+       * Sets the key for the item to \a key.
+       */
+      void setKey(const String &key);
+
+      /*!
+       * Sets the value of the item to \a value and clears any previous contents.
+       *
+       * \see toString()
+       */
+      void setValue(const String &value);
+
+      /*!
+       * Sets the value of the item to the list of values in \a value and clears
+       * any previous contents.
+       *
+       * \see toStringList()
+       */
+      void setValues(const StringList &values);
+
+      /*!
+       * Appends \a value to create (or extend) the current list of values.
+       *
+       * \see toString()
+       */
+      void appendValue(const String &value);
+
+      /*!
+       * Appends \a values to extend the current list of values.
+       *
+       * \see toStringList()
+       */
+      void appendValues(const StringList &values);
+
+      /*!
+       * Returns the size of the full item.
+       */
+      int size() const;
+
+      /*!
+       * Returns the value as a single string. In case of multiple strings,
+       * the first is returned.
+       */
+      String toString() const;
+
+      /*!
+       * \deprecated
+       * \see values
+       */
+      StringList toStringList() const;
+
+      /*!
+       * Returns the list of values.
+       */
+      StringList values() const;
+
+      /*!
+       * Render the item to a ByteVector.
+       */
+      ByteVector render() const;
+
+      /*!
+       * Parse the item from the ByteVector \a data.
+       */
+      void parse(const ByteVector& data);
+
+      /*!
+       * Set the item to read-only.
+       */
+      void setReadOnly(bool readOnly);
+
+      /*!
+       * Return true if the item is read-only.
+       */
+      bool isReadOnly() const;
+
+      /*!
+       * Sets the type of the item to \a type.
+       *
+       * \see ItemTypes
+       */
+      void setType(ItemTypes type);
+
+      /*!
+       * Returns the type of the item.
+       */
+      ItemTypes type() const;
+
+      /*!
+       * Returns if the item has any real content.
+       */
+      bool isEmpty() const;
+
+    private:
+      class ItemPrivate;
+      ItemPrivate *d;
+    };
+  }
+
+}
+
+#endif
+
+
diff --git a/src/taglib/ape/apetag.cpp b/src/taglib/ape/apetag.cpp
new file mode 100644 (file)
index 0000000..1c38d2b
--- /dev/null
@@ -0,0 +1,266 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef __SUNPRO_CC
+// Sun Studio finds multiple specializations of Map because
+// it considers specializations with and without class types
+// to be different; this define forces Map to use only the
+// specialization with the class keyword.
+#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
+#endif
+
+#include <tfile.h>
+#include <tstring.h>
+#include <tmap.h>
+
+#include "apetag.h"
+#include "apefooter.h"
+#include "apeitem.h"
+
+using namespace TagLib;
+using namespace APE;
+
+class APE::Tag::TagPrivate
+{
+public:
+  TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
+
+  File *file;
+  long footerLocation;
+  long tagLength;
+
+  Footer footer;
+
+  ItemListMap itemListMap;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+APE::Tag::Tag() : TagLib::Tag()
+{
+  d = new TagPrivate;
+}
+
+APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag()
+{
+  d = new TagPrivate;
+  d->file = file;
+  d->footerLocation = footerLocation;
+
+  read();
+}
+
+APE::Tag::~Tag()
+{
+  delete d;
+}
+
+ByteVector APE::Tag::fileIdentifier()
+{
+  return ByteVector::fromCString("APETAGEX");
+}
+
+String APE::Tag::title() const
+{
+  if(d->itemListMap["TITLE"].isEmpty())
+    return String::null;
+  return d->itemListMap["TITLE"].toString();
+}
+
+String APE::Tag::artist() const
+{
+  if(d->itemListMap["ARTIST"].isEmpty())
+    return String::null;
+  return d->itemListMap["ARTIST"].toString();
+}
+
+String APE::Tag::album() const
+{
+  if(d->itemListMap["ALBUM"].isEmpty())
+    return String::null;
+  return d->itemListMap["ALBUM"].toString();
+}
+
+String APE::Tag::comment() const
+{
+  if(d->itemListMap["COMMENT"].isEmpty())
+    return String::null;
+  return d->itemListMap["COMMENT"].toString();
+}
+
+String APE::Tag::genre() const
+{
+  if(d->itemListMap["GENRE"].isEmpty())
+    return String::null;
+  return d->itemListMap["GENRE"].toString();
+}
+
+TagLib::uint APE::Tag::year() const
+{
+  if(d->itemListMap["YEAR"].isEmpty())
+    return 0;
+  return d->itemListMap["YEAR"].toString().toInt();
+}
+
+TagLib::uint APE::Tag::track() const
+{
+  if(d->itemListMap["TRACK"].isEmpty())
+    return 0;
+  return d->itemListMap["TRACK"].toString().toInt();
+}
+
+void APE::Tag::setTitle(const String &s)
+{
+  addValue("TITLE", s, true);
+}
+
+void APE::Tag::setArtist(const String &s)
+{
+  addValue("ARTIST", s, true);
+}
+
+void APE::Tag::setAlbum(const String &s)
+{
+  addValue("ALBUM", s, true);
+}
+
+void APE::Tag::setComment(const String &s)
+{
+  addValue("COMMENT", s, true);
+}
+
+void APE::Tag::setGenre(const String &s)
+{
+  addValue("GENRE", s, true);
+}
+
+void APE::Tag::setYear(uint i)
+{
+  if(i <= 0)
+    removeItem("YEAR");
+  else
+    addValue("YEAR", String::number(i), true);
+}
+
+void APE::Tag::setTrack(uint i)
+{
+  if(i <= 0)
+    removeItem("TRACK");
+  else
+    addValue("TRACK", String::number(i), true);
+}
+
+APE::Footer *APE::Tag::footer() const
+{
+  return &d->footer;
+}
+
+const APE::ItemListMap& APE::Tag::itemListMap() const
+{
+  return d->itemListMap;
+}
+
+void APE::Tag::removeItem(const String &key)
+{
+  Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
+  if(it != d->itemListMap.end())
+    d->itemListMap.erase(it);
+}
+
+void APE::Tag::addValue(const String &key, const String &value, bool replace)
+{
+  if(replace)
+    removeItem(key);
+  if(!value.isEmpty()) {
+    if(d->itemListMap.contains(key) || !replace)
+      d->itemListMap[key.upper()].appendValue(value);
+    else
+      setItem(key, Item(key, value));
+  }
+}
+
+void APE::Tag::setItem(const String &key, const Item &item)
+{
+  d->itemListMap.insert(key.upper(), item);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+void APE::Tag::read()
+{
+  if(d->file && d->file->isValid()) {
+
+    d->file->seek(d->footerLocation);
+    d->footer.setData(d->file->readBlock(Footer::size()));
+
+    if(d->footer.tagSize() <= Footer::size() ||
+       d->footer.tagSize() > uint(d->file->length()))
+      return;
+
+    d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
+    parse(d->file->readBlock(d->footer.tagSize() - Footer::size()));
+  }
+}
+
+ByteVector APE::Tag::render() const
+{
+  ByteVector data;
+  uint itemCount = 0;
+
+  {
+    for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
+        it != d->itemListMap.end(); ++it)
+    {
+      data.append(it->second.render());
+      itemCount++;
+    }
+  }
+
+  d->footer.setItemCount(itemCount);
+  d->footer.setTagSize(data.size() + Footer::size());
+  d->footer.setHeaderPresent(true);
+
+  return d->footer.renderHeader() + data + d->footer.renderFooter();
+}
+
+void APE::Tag::parse(const ByteVector &data)
+{
+  uint pos = 0;
+
+  // 11 bytes is the minimum size for an APE item
+
+  for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
+    APE::Item item;
+    item.parse(data.mid(pos));
+
+    d->itemListMap.insert(item.key().upper(), item);
+
+    pos += item.size();
+  }
+}
diff --git a/src/taglib/ape/apetag.h b/src/taglib/ape/apetag.h
new file mode 100644 (file)
index 0000000..03a3c91
--- /dev/null
@@ -0,0 +1,162 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_APETAG_H
+#define TAGLIB_APETAG_H
+
+#include "tag.h"
+#include "tbytevector.h"
+#include "tmap.h"
+#include "tstring.h"
+#include "taglib_export.h"
+
+#include "apeitem.h"
+
+namespace TagLib {
+
+  class File;
+
+  //! An implementation of the APE tagging format
+
+  namespace APE {
+
+    class Footer;
+
+    /*!
+     * A mapping between a list of item names, or keys, and the associated item.
+     *
+     * \see APE::Tag::itemListMap()
+     */
+    typedef Map<const String, Item> ItemListMap;
+
+
+    //! An APE tag implementation
+
+    class TAGLIB_EXPORT Tag : public TagLib::Tag
+    {
+    public:
+      /*!
+       * Create an APE tag with default values.
+       */
+      Tag();
+
+      /*!
+       * Create an APE tag and parse the data in \a file with APE footer at
+       * \a tagOffset.
+       */
+      Tag(File *file, long footerLocation);
+
+      /*!
+       * Destroys this Tag instance.
+       */
+      virtual ~Tag();
+
+      /*!
+       * Renders the in memory values to a ByteVector suitable for writing to
+       * the file.
+       */
+      ByteVector render() const;
+
+      /*!
+       * Returns the string "APETAGEX" suitable for usage in locating the tag in a
+       * file.
+       */
+      static ByteVector fileIdentifier();
+
+      // Reimplementations.
+
+      virtual String title() const;
+      virtual String artist() const;
+      virtual String album() const;
+      virtual String comment() const;
+      virtual String genre() const;
+      virtual uint year() const;
+      virtual uint track() const;
+
+      virtual void setTitle(const String &s);
+      virtual void setArtist(const String &s);
+      virtual void setAlbum(const String &s);
+      virtual void setComment(const String &s);
+      virtual void setGenre(const String &s);
+      virtual void setYear(uint i);
+      virtual void setTrack(uint i);
+
+      /*!
+       * Returns a pointer to the tag's footer.
+       */
+      Footer *footer() const;
+
+      /*!
+       * Returns a reference to the item list map.  This is an ItemListMap of
+       * all of the items in the tag.
+       *
+       * This is the most powerfull structure for accessing the items of the tag.
+       *
+       * \warning You should not modify this data structure directly, instead
+       * use setItem() and removeItem().
+       */
+      const ItemListMap &itemListMap() const;
+
+      /*!
+       * Removes the \a key item from the tag
+       */
+      void removeItem(const String &key);
+
+      /*!
+       * Adds to the item specified by \a key the data \a value.  If \a replace
+       * is true, then all of the other values on the same key will be removed
+       * first.
+       */
+      void addValue(const String &key, const String &value, bool replace = true);
+
+      /*!
+       * Sets the \a key item to the value of \a item. If an item with the \a key is already
+       * present, it will be replaced.
+       */
+      void setItem(const String &key, const Item &item);
+
+    protected:
+
+      /*!
+       * Reads from the file specified in the constructor.
+       */
+      void read();
+
+      /*!
+       * Parses the body of the tag in \a data.
+       */
+      void parse(const ByteVector &data);
+
+    private:
+      Tag(const Tag &);
+      Tag &operator=(const Tag &);
+
+      class TagPrivate;
+      TagPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/asf/asfattribute.cpp b/src/taglib/asf/asfattribute.cpp
new file mode 100644 (file)
index 0000000..85db872
--- /dev/null
@@ -0,0 +1,342 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include <taglib.h>
+#include <tdebug.h>
+#include "asfattribute.h"
+#include "asffile.h"
+
+using namespace TagLib;
+
+class ASF::Attribute::AttributePrivate : public RefCounter
+{
+public:
+  AttributePrivate()
+    : stream(0),
+      language(0) {}
+  AttributeTypes type;
+  String stringValue;
+  ByteVector byteVectorValue;
+  union {
+    unsigned int intValue;
+    unsigned short shortValue;
+    unsigned long long longLongValue;
+    bool boolValue;
+  };
+  int stream;
+  int language;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::Attribute::Attribute()
+{
+  d = new AttributePrivate;
+  d->type = UnicodeType;
+}
+
+ASF::Attribute::Attribute(const ASF::Attribute &other)
+  : d(other.d)
+{
+  d->ref();
+}
+
+ASF::Attribute &
+ASF::Attribute::operator=(const ASF::Attribute &other)
+{
+  if(d->deref())
+    delete d;
+  d = other.d;
+  d->ref();
+  return *this;
+}
+
+ASF::Attribute::~Attribute()
+{
+  if(d->deref())
+    delete d;
+}
+
+ASF::Attribute::Attribute(const String &value)
+{
+  d = new AttributePrivate;
+  d->type = UnicodeType;
+  d->stringValue = value;
+}
+
+ASF::Attribute::Attribute(const ByteVector &value)
+{
+  d = new AttributePrivate;
+  d->type = BytesType;
+  d->byteVectorValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned int value)
+{
+  d = new AttributePrivate;
+  d->type = DWordType;
+  d->intValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned long long value)
+{
+  d = new AttributePrivate;
+  d->type = QWordType;
+  d->longLongValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned short value)
+{
+  d = new AttributePrivate;
+  d->type = WordType;
+  d->shortValue = value;
+}
+
+ASF::Attribute::Attribute(bool value)
+{
+  d = new AttributePrivate;
+  d->type = BoolType;
+  d->boolValue = value;
+}
+
+ASF::Attribute::AttributeTypes
+ASF::Attribute::type() const
+{
+  return d->type;
+}
+
+String
+ASF::Attribute::toString() const
+{
+  return d->stringValue;
+}
+
+ByteVector
+ASF::Attribute::toByteVector() const
+{
+  return d->byteVectorValue;
+}
+
+unsigned short
+ASF::Attribute::toBool() const
+{
+  return d->shortValue;
+}
+
+unsigned short
+ASF::Attribute::toUShort() const
+{
+  return d->shortValue;
+}
+
+unsigned int
+ASF::Attribute::toUInt() const
+{
+  return d->intValue;
+}
+
+unsigned long long
+ASF::Attribute::toULongLong() const
+{
+  return d->longLongValue;
+}
+
+String
+ASF::Attribute::parse(ASF::File &f, int kind)
+{
+  int size, nameLength;
+  String name;
+
+  // extended content descriptor
+  if(kind == 0) {
+    nameLength = f.readWORD();
+    name = f.readString(nameLength);
+    d->type = ASF::Attribute::AttributeTypes(f.readWORD());
+    size = f.readWORD();
+  }
+  // metadata & metadata library
+  else {
+    int temp = f.readWORD();
+    // metadata library
+    if(kind == 2) {
+      d->language = temp;
+    }
+    d->stream = f.readWORD();
+    nameLength = f.readWORD();
+    d->type = ASF::Attribute::AttributeTypes(f.readWORD());
+    size = f.readDWORD();
+    name = f.readString(nameLength);
+  }
+
+  if(kind != 2 && size > 65535) {
+    debug("ASF::Attribute::parse() -- Value larger than 64kB");
+  }
+
+  switch(d->type) {
+  case WordType:
+    d->shortValue = f.readWORD();
+    break;
+
+  case BoolType:
+    if(kind == 0) {
+      d->boolValue = f.readDWORD() == 1;
+    }
+    else {
+      d->boolValue = f.readWORD() == 1;
+    }
+    break;
+
+  case DWordType:
+    d->intValue = f.readDWORD();
+    break;
+
+  case QWordType:
+    d->longLongValue = f.readQWORD();
+    break;
+
+  case UnicodeType:
+    d->stringValue = f.readString(size);
+    break;
+
+  case BytesType:
+  case GuidType:
+    d->byteVectorValue = f.readBlock(size);
+    break;
+  }
+
+  return name;
+}
+
+int
+ASF::Attribute::dataSize() const
+{
+  switch (d->type) {
+  case WordType:
+    return 2;
+  case BoolType:
+    return 4;
+  case DWordType:
+    return 4;
+  case QWordType:
+    return 5;
+  case UnicodeType:
+    return d->stringValue.size() * 2 + 2;
+  case BytesType:
+  case GuidType:
+    return d->byteVectorValue.size();
+  }
+  return 0;
+}
+
+ByteVector
+ASF::Attribute::render(const String &name, int kind) const
+{
+  ByteVector data;
+
+  switch (d->type) {
+  case WordType:
+    data.append(ByteVector::fromShort(d->shortValue, false));
+    break;
+
+  case BoolType:
+    if(kind == 0) {
+      data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
+    }
+    else {
+      data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
+    }
+    break;
+
+  case DWordType:
+    data.append(ByteVector::fromUInt(d->intValue, false));
+    break;
+
+  case QWordType:
+    data.append(ByteVector::fromLongLong(d->longLongValue, false));
+    break;
+
+  case UnicodeType:
+    data.append(File::renderString(d->stringValue));
+    break;
+
+  case BytesType:
+  case GuidType:
+    data.append(d->byteVectorValue);
+    break;
+  }
+
+  if(kind == 0) {
+    data = File::renderString(name, true) +
+           ByteVector::fromShort((int)d->type, false) +
+           ByteVector::fromShort(data.size(), false) +
+           data;
+  }
+  else {
+    ByteVector nameData = File::renderString(name);
+    data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
+           ByteVector::fromShort(d->stream, false) +
+           ByteVector::fromShort(nameData.size(), false) +
+           ByteVector::fromShort((int)d->type, false) +
+           ByteVector::fromUInt(data.size(), false) +
+           nameData +
+           data;
+  }
+
+  return data;
+}
+
+int
+ASF::Attribute::language() const
+{
+  return d->language;
+}
+
+void
+ASF::Attribute::setLanguage(int value)
+{
+  d->language = value;
+}
+
+int
+ASF::Attribute::stream() const
+{
+  return d->stream;
+}
+
+void
+ASF::Attribute::setStream(int value)
+{
+  d->stream = value;
+}
+
+#endif
diff --git a/src/taglib/asf/asfattribute.h b/src/taglib/asf/asfattribute.h
new file mode 100644 (file)
index 0000000..9752389
--- /dev/null
@@ -0,0 +1,184 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFATTRIBUTE_H
+#define TAGLIB_ASFATTRIBUTE_H
+
+#include "tstring.h"
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib
+{
+
+  namespace ASF
+  {
+
+    class File;
+
+    class TAGLIB_EXPORT Attribute
+    {
+    public:
+
+      /*!
+       * Enum of types an Attribute can have.
+       */
+      enum AttributeTypes {
+        UnicodeType = 0,
+        BytesType   = 1,
+        BoolType    = 2,
+        DWordType   = 3,
+        QWordType   = 4,
+        WordType    = 5,
+        GuidType    = 6
+      };
+
+      /*!
+       * Constructs an empty attribute.
+       */
+      Attribute();
+
+      /*!
+       * Constructs an attribute with \a key and a UnicodeType \a value.
+       */
+      Attribute(const String &value);
+
+      /*!
+       * Constructs an attribute with \a key and a BytesType \a value.
+       */
+      Attribute(const ByteVector &value);
+
+      /*!
+       * Constructs an attribute with \a key and a DWordType \a value.
+       */
+      Attribute(unsigned int value);
+
+      /*!
+       * Constructs an attribute with \a key and a QWordType \a value.
+       */
+      Attribute(unsigned long long value);
+
+      /*!
+       * Constructs an attribute with \a key and a WordType \a value.
+       */
+      Attribute(unsigned short value);
+
+      /*!
+       * Constructs an attribute with \a key and a BoolType \a value.
+       */
+      Attribute(bool value);
+
+      /*!
+       * Construct an attribute as a copy of \a other.
+       */
+      Attribute(const Attribute &item);
+
+      /*!
+       * Copies the contents of \a other into this item.
+       */
+      ASF::Attribute &operator=(const Attribute &other);
+
+      /*!
+       * Destroys the attribute.
+       */
+      virtual ~Attribute();
+
+      /*!
+       * Returns type of the value.
+       */
+      AttributeTypes type() const;
+
+      /*!
+       * Returns the BoolType \a value.
+       */
+      unsigned short toBool() const;
+
+      /*!
+       * Returns the WordType \a value.
+       */
+      unsigned short toUShort() const;
+
+      /*!
+       * Returns the DWordType \a value.
+       */
+      unsigned int toUInt() const;
+
+      /*!
+       * Returns the QWordType \a value.
+       */
+      unsigned long long toULongLong() const;
+
+      /*!
+       * Returns the UnicodeType \a value.
+       */
+      String toString() const;
+
+      /*!
+       * Returns the BytesType \a value.
+       */
+      ByteVector toByteVector() const;
+
+      /*!
+       * Returns the language number, or 0 is no stream number was set.
+       */
+      int language() const;
+
+      /*!
+       * Sets the language number.
+       */
+      void setLanguage(int value);
+
+      /*!
+       * Returns the stream number, or 0 is no stream number was set.
+       */
+      int stream() const;
+
+      /*!
+       * Sets the stream number.
+       */
+      void setStream(int value);
+
+#ifndef DO_NOT_DOCUMENT
+      /* THIS IS PRIVATE, DON'T TOUCH IT! */
+      String parse(ASF::File &file, int kind = 0);
+#endif
+
+      //! Returns the size of the stored data
+      int dataSize() const;
+
+    private:
+      friend class File;
+
+      ByteVector render(const String &name, int kind = 0) const;
+
+      class AttributePrivate;
+      AttributePrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/src/taglib/asf/asffile.cpp b/src/taglib/asf/asffile.cpp
new file mode 100644 (file)
index 0000000..012f39b
--- /dev/null
@@ -0,0 +1,563 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include <tdebug.h>
+#include <tbytevectorlist.h>
+#include <tstring.h>
+#include "asffile.h"
+#include "asftag.h"
+#include "asfproperties.h"
+
+using namespace TagLib;
+
+class ASF::File::FilePrivate
+{
+public:
+  FilePrivate():
+    size(0),
+    tag(0),
+    properties(0),
+    contentDescriptionObject(0),
+    extendedContentDescriptionObject(0),
+    headerExtensionObject(0),
+    metadataObject(0),
+    metadataLibraryObject(0) {}
+  unsigned long long size;
+  ASF::Tag *tag;
+  ASF::Properties *properties;
+  List<ASF::File::BaseObject *> objects;
+  ASF::File::ContentDescriptionObject *contentDescriptionObject;
+  ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
+  ASF::File::HeaderExtensionObject *headerExtensionObject;
+  ASF::File::MetadataObject *metadataObject;
+  ASF::File::MetadataLibraryObject *metadataLibraryObject;
+};
+
+static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
+static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
+static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
+static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
+static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
+static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
+
+class ASF::File::BaseObject
+{
+public:
+  ByteVector data;
+  virtual ~BaseObject() {}
+  virtual ByteVector guid() = 0;
+  virtual void parse(ASF::File *file, unsigned int size);
+  virtual ByteVector render(ASF::File *file);
+};
+
+class ASF::File::UnknownObject : public ASF::File::BaseObject
+{
+  ByteVector myGuid;
+public:
+  UnknownObject(const ByteVector &guid);
+  ByteVector guid();
+};
+
+class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
+{
+public:
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+};
+
+class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
+{
+public:
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+};
+
+class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
+{
+public:
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject
+{
+public:
+  ByteVectorList attributeData;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::MetadataObject : public ASF::File::BaseObject
+{
+public:
+  ByteVectorList attributeData;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject
+{
+public:
+  ByteVectorList attributeData;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
+{
+public:
+  List<ASF::File::BaseObject *> objects;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+void
+ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
+{
+  data = file->readBlock(size - 24);
+}
+
+ByteVector
+ASF::File::BaseObject::render(ASF::File * /*file*/)
+{
+  return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
+}
+
+ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
+{
+}
+
+ByteVector
+ASF::File::UnknownObject::guid()
+{
+  return myGuid;
+}
+
+ByteVector
+ASF::File::FilePropertiesObject::guid()
+{
+  return filePropertiesGuid;
+}
+
+void
+ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
+{
+  BaseObject::parse(file, size);
+  file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
+}
+
+ByteVector
+ASF::File::StreamPropertiesObject::guid()
+{
+  return streamPropertiesGuid;
+}
+
+void
+ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
+{
+  BaseObject::parse(file, size);
+  file->d->properties->setChannels(data.mid(56, 2).toShort(false));
+  file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
+  file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
+}
+
+ByteVector
+ASF::File::ContentDescriptionObject::guid()
+{
+  return contentDescriptionGuid;
+}
+
+void
+ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->contentDescriptionObject = this;
+  int titleLength = file->readWORD();
+  int artistLength = file->readWORD();
+  int copyrightLength = file->readWORD();
+  int commentLength = file->readWORD();
+  int ratingLength = file->readWORD();
+  file->d->tag->setTitle(file->readString(titleLength));
+  file->d->tag->setArtist(file->readString(artistLength));
+  file->d->tag->setCopyright(file->readString(copyrightLength));
+  file->d->tag->setComment(file->readString(commentLength));
+  file->d->tag->setRating(file->readString(ratingLength));
+}
+
+ByteVector
+ASF::File::ContentDescriptionObject::render(ASF::File *file)
+{
+  ByteVector v1 = file->renderString(file->d->tag->title());
+  ByteVector v2 = file->renderString(file->d->tag->artist());
+  ByteVector v3 = file->renderString(file->d->tag->copyright());
+  ByteVector v4 = file->renderString(file->d->tag->comment());
+  ByteVector v5 = file->renderString(file->d->tag->rating());
+  data.clear();
+  data.append(ByteVector::fromShort(v1.size(), false));
+  data.append(ByteVector::fromShort(v2.size(), false));
+  data.append(ByteVector::fromShort(v3.size(), false));
+  data.append(ByteVector::fromShort(v4.size(), false));
+  data.append(ByteVector::fromShort(v5.size(), false));
+  data.append(v1);
+  data.append(v2);
+  data.append(v3);
+  data.append(v4);
+  data.append(v5);
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::ExtendedContentDescriptionObject::guid()
+{
+  return extendedContentDescriptionGuid;
+}
+
+void
+ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->extendedContentDescriptionObject = this;
+  int count = file->readWORD();
+  while(count--) {
+    ASF::Attribute attribute;
+    String name = attribute.parse(*file);
+    file->d->tag->addAttribute(name, attribute);
+  }
+}
+
+ByteVector
+ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
+{
+  data.clear();
+  data.append(ByteVector::fromShort(attributeData.size(), false));
+  data.append(attributeData.toByteVector(ByteVector::null));
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::MetadataObject::guid()
+{
+  return metadataGuid;
+}
+
+void
+ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->metadataObject = this;
+  int count = file->readWORD();
+  while(count--) {
+    ASF::Attribute attribute;
+    String name = attribute.parse(*file, 1);
+    file->d->tag->addAttribute(name, attribute);
+  }
+}
+
+ByteVector
+ASF::File::MetadataObject::render(ASF::File *file)
+{
+  data.clear();
+  data.append(ByteVector::fromShort(attributeData.size(), false));
+  data.append(attributeData.toByteVector(ByteVector::null));
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::MetadataLibraryObject::guid()
+{
+  return metadataLibraryGuid;
+}
+
+void
+ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->metadataLibraryObject = this;
+  int count = file->readWORD();
+  while(count--) {
+    ASF::Attribute attribute;
+    String name = attribute.parse(*file, 2);
+    file->d->tag->addAttribute(name, attribute);
+  }
+}
+
+ByteVector
+ASF::File::MetadataLibraryObject::render(ASF::File *file)
+{
+  data.clear();
+  data.append(ByteVector::fromShort(attributeData.size(), false));
+  data.append(attributeData.toByteVector(ByteVector::null));
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::HeaderExtensionObject::guid()
+{
+  return headerExtensionGuid;
+}
+
+void
+ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->headerExtensionObject = this;
+  file->seek(18, File::Current);
+  long long dataSize = file->readDWORD();
+  long long dataPos = 0;
+  while(dataPos < dataSize) {
+    ByteVector guid = file->readBlock(16);
+    long long size = file->readQWORD();
+    BaseObject *obj;
+    if(guid == metadataGuid) {
+      obj = new MetadataObject();
+    }
+    else if(guid == metadataLibraryGuid) {
+      obj = new MetadataLibraryObject();
+    }
+    else {
+      obj = new UnknownObject(guid);
+    }
+    obj->parse(file, size);
+    objects.append(obj);
+    dataPos += size;
+  }
+}
+
+ByteVector
+ASF::File::HeaderExtensionObject::render(ASF::File *file)
+{
+  data.clear();
+  for(unsigned int i = 0; i < objects.size(); i++) {
+    data.append(objects[i]->render(file));
+  }
+  data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
+  return BaseObject::render(file);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) 
+  : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+ASF::File::~File()
+{
+  for(unsigned int i = 0; i < d->objects.size(); i++) {
+    delete d->objects[i];
+  }
+  if(d->tag) {
+    delete d->tag;
+  }
+  if(d->properties) {
+    delete d->properties;
+  }
+  delete d;
+}
+
+ASF::Tag *ASF::File::tag() const
+{
+  return d->tag;
+}
+
+ASF::Properties *ASF::File::audioProperties() const
+{
+  return d->properties;
+}
+
+void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
+{
+  if(!isValid())
+    return;
+
+  ByteVector guid = readBlock(16);
+  if(guid != headerGuid) {
+    debug("ASF: Not an ASF file.");
+    return;
+  }
+
+  d->tag = new ASF::Tag();
+  d->properties = new ASF::Properties();
+
+  d->size = readQWORD();
+  int numObjects = readDWORD();
+  seek(2, Current);
+
+  for(int i = 0; i < numObjects; i++) {
+    ByteVector guid = readBlock(16);
+    long size = (long)readQWORD();
+    BaseObject *obj;
+    if(guid == filePropertiesGuid) {
+      obj = new FilePropertiesObject();
+    }
+    else if(guid == streamPropertiesGuid) {
+      obj = new StreamPropertiesObject();
+    }
+    else if(guid == contentDescriptionGuid) {
+      obj = new ContentDescriptionObject();
+    }
+    else if(guid == extendedContentDescriptionGuid) {
+      obj = new ExtendedContentDescriptionObject();
+    }
+    else if(guid == headerExtensionGuid) {
+      obj = new HeaderExtensionObject();
+    }
+    else {
+      obj = new UnknownObject(guid);
+    }
+    obj->parse(this, size);
+    d->objects.append(obj);
+  }
+}
+
+bool ASF::File::save()
+{
+  if(readOnly()) {
+    debug("ASF: File is read-only.");
+    return false;
+  }
+
+  if(!d->contentDescriptionObject) {
+    d->contentDescriptionObject = new ContentDescriptionObject();
+    d->objects.append(d->contentDescriptionObject);
+  }
+  if(!d->extendedContentDescriptionObject) {
+    d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
+    d->objects.append(d->extendedContentDescriptionObject);
+  }
+  if(!d->headerExtensionObject) {
+    d->headerExtensionObject = new HeaderExtensionObject();
+    d->objects.append(d->headerExtensionObject);
+  }
+  if(!d->metadataObject) {
+    d->metadataObject = new MetadataObject();
+    d->headerExtensionObject->objects.append(d->metadataObject);
+  }
+  if(!d->metadataLibraryObject) {
+    d->metadataLibraryObject = new MetadataLibraryObject();
+    d->headerExtensionObject->objects.append(d->metadataLibraryObject);
+  }
+
+  ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
+  for(; it != d->tag->attributeListMap().end(); it++) {
+    const String &name = it->first;
+    const AttributeList &attributes = it->second;
+    bool inExtendedContentDescriptionObject = false;
+    bool inMetadataObject = false;
+    for(unsigned int j = 0; j < attributes.size(); j++) {
+      const Attribute &attribute = attributes[j];
+      bool largeValue = attribute.dataSize() > 65535;
+      if(!inExtendedContentDescriptionObject && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
+        d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
+        inExtendedContentDescriptionObject = true;
+      }
+      else if(!inMetadataObject && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
+        d->metadataObject->attributeData.append(attribute.render(name, 1));
+        inMetadataObject = true;
+      }
+      else {
+        d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
+      }
+    }
+  }
+
+  ByteVector data;
+  for(unsigned int i = 0; i < d->objects.size(); i++) {
+    data.append(d->objects[i]->render(this));
+  }
+  data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
+  insert(data, 0, d->size);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+int ASF::File::readBYTE()
+{
+  ByteVector v = readBlock(1);
+  return v[0];
+}
+
+int ASF::File::readWORD()
+{
+  ByteVector v = readBlock(2);
+  return v.toShort(false);
+}
+
+unsigned int ASF::File::readDWORD()
+{
+  ByteVector v = readBlock(4);
+  return v.toUInt(false);
+}
+
+long long ASF::File::readQWORD()
+{
+  ByteVector v = readBlock(8);
+  return v.toLongLong(false);
+}
+
+String
+ASF::File::readString(int length)
+{
+  ByteVector data = readBlock(length);
+  unsigned int size = data.size();
+  while (size >= 2) {
+    if(data[size - 1] != '\0' || data[size - 2] != '\0') {
+      break;
+    }
+    size -= 2;
+  }
+  if(size != data.size()) {
+    data.resize(size);
+  }
+  return String(data, String::UTF16LE);
+}
+
+ByteVector
+ASF::File::renderString(const String &str, bool includeLength)
+{
+  ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
+  if(includeLength) {
+    data = ByteVector::fromShort(data.size(), false) + data;
+  }
+  return data;
+}
+
+#endif
diff --git a/src/taglib/asf/asffile.h b/src/taglib/asf/asffile.h
new file mode 100644 (file)
index 0000000..23a6d00
--- /dev/null
@@ -0,0 +1,119 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFFILE_H
+#define TAGLIB_ASFFILE_H
+
+#include "tag.h"
+#include "tfile.h"
+#include "taglib_export.h"
+#include "asfproperties.h"
+#include "asftag.h"
+
+namespace TagLib {
+
+  //! An implementation of ASF (WMA) metadata
+  namespace ASF {
+
+    /*!
+     * This implements and provides an interface for ASF files to the
+     * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+     * the abstract TagLib::File API as well as providing some additional
+     * information specific to ASF files.
+     */
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+
+      /*!
+       * Contructs an ASF file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * \note In the current implementation, both \a readProperties and
+       * \a propertiesStyle are ignored.
+       */
+      File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns a pointer to the ASF tag of the file.
+       *
+       * ASF::Tag implements the tag interface, so this serves as the
+       * reimplementation of TagLib::File::tag().
+       *
+       * \note The Tag <b>is still</b> owned by the ASF::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      virtual Tag *tag() const;
+
+      /*!
+       * Returns the ASF audio properties for this file.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Save the file.
+       *
+       * This returns true if the save was successful.
+       */
+      virtual bool save();
+
+    private:
+
+      int readBYTE();
+      int readWORD();
+      unsigned int readDWORD();
+      long long readQWORD();
+      static ByteVector renderString(const String &str, bool includeLength = false);
+      String readString(int len);
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+      friend class Attribute;
+
+      class BaseObject;
+      class UnknownObject;
+      class FilePropertiesObject;
+      class StreamPropertiesObject;
+      class ContentDescriptionObject;
+      class ExtendedContentDescriptionObject;
+      class HeaderExtensionObject;
+      class MetadataObject;
+      class MetadataLibraryObject;
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/src/taglib/asf/asfproperties.cpp b/src/taglib/asf/asfproperties.cpp
new file mode 100644 (file)
index 0000000..68e2d4c
--- /dev/null
@@ -0,0 +1,107 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "asfproperties.h"
+
+using namespace TagLib;
+
+class ASF::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0) {}
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
+{
+  d = new PropertiesPrivate;
+}
+
+ASF::Properties::~Properties()
+{
+  if(d)
+    delete d;  
+}
+
+int ASF::Properties::length() const
+{
+  return d->length;
+}
+
+int ASF::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int ASF::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int ASF::Properties::channels() const
+{
+  return d->channels;
+} 
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void ASF::Properties::setLength(int length)
+{
+  d->length = length;
+}
+
+void ASF::Properties::setBitrate(int length)
+{
+  d->bitrate = length;
+}
+
+void ASF::Properties::setSampleRate(int length)
+{
+  d->sampleRate = length;
+}
+
+void ASF::Properties::setChannels(int length)
+{
+  d->channels = length;
+}
+
+#endif
diff --git a/src/taglib/asf/asfproperties.h b/src/taglib/asf/asfproperties.h
new file mode 100644 (file)
index 0000000..653b015
--- /dev/null
@@ -0,0 +1,74 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFPROPERTIES_H
+#define TAGLIB_ASFPROPERTIES_H
+
+#include "audioproperties.h"
+#include "tstring.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ASF {
+
+    //! An implementation of ASF audio properties
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+
+      /*!
+       * Create an instance of ASF::Properties.
+       */
+      Properties();
+
+      /*!
+       * Destroys this ASF::Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+#ifndef DO_NOT_DOCUMENT
+      void setLength(int value);
+      void setBitrate(int value);
+      void setSampleRate(int value);
+      void setChannels(int value);
+#endif
+
+    private:
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+
+  }
+
+}
+
+#endif 
diff --git a/src/taglib/asf/asftag.cpp b/src/taglib/asf/asftag.cpp
new file mode 100644 (file)
index 0000000..f910ccb
--- /dev/null
@@ -0,0 +1,219 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include "asftag.h"
+
+using namespace TagLib;
+
+class ASF::Tag::TagPrivate
+{
+public:
+  String title;
+  String artist;
+  String copyright;
+  String comment;
+  String rating;
+  AttributeListMap attributeListMap;
+};
+
+ASF::Tag::Tag()
+: TagLib::Tag()
+{
+  d = new TagPrivate;
+}
+
+ASF::Tag::~Tag()
+{
+  if(d)
+    delete d;
+}
+
+String
+ASF::Tag::title() const
+{
+  return d->title;
+}
+
+String
+ASF::Tag::artist() const
+{
+  return d->artist;
+}
+
+String
+ASF::Tag::album() const
+{
+  if(d->attributeListMap.contains("WM/AlbumTitle"))
+    return d->attributeListMap["WM/AlbumTitle"][0].toString();
+  return String::null;
+}
+
+String
+ASF::Tag::copyright() const
+{
+  return d->copyright;
+}
+
+String
+ASF::Tag::comment() const
+{
+  return d->comment;
+}
+
+String
+ASF::Tag::rating() const
+{
+  return d->rating;
+}
+
+unsigned int
+ASF::Tag::year() const
+{
+  if(d->attributeListMap.contains("WM/Year"))
+    return d->attributeListMap["WM/Year"][0].toString().toInt();
+  return 0;
+}
+
+unsigned int
+ASF::Tag::track() const
+{
+  if(d->attributeListMap.contains("WM/TrackNumber")) {
+    const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
+    if(attr.type() == ASF::Attribute::DWordType)
+      return attr.toUInt();
+    else
+      return attr.toString().toInt();
+  }
+  if(d->attributeListMap.contains("WM/Track"))
+    return d->attributeListMap["WM/Track"][0].toUInt();
+  return 0;
+}
+
+String
+ASF::Tag::genre() const
+{
+  if(d->attributeListMap.contains("WM/Genre"))
+    return d->attributeListMap["WM/Genre"][0].toString();
+  return String::null;
+}
+
+void
+ASF::Tag::setTitle(const String &value)
+{
+  d->title = value;
+}
+
+void
+ASF::Tag::setArtist(const String &value)
+{
+  d->artist = value;
+}
+
+void
+ASF::Tag::setCopyright(const String &value)
+{
+  d->copyright = value;
+}
+
+void
+ASF::Tag::setComment(const String &value)
+{
+  d->comment = value;
+}
+
+void
+ASF::Tag::setRating(const String &value)
+{
+  d->rating = value;
+}
+
+void
+ASF::Tag::setAlbum(const String &value)
+{
+  setAttribute("WM/AlbumTitle", value);
+}
+
+void
+ASF::Tag::setGenre(const String &value)
+{
+  setAttribute("WM/Genre", value);
+}
+
+void
+ASF::Tag::setYear(uint value)
+{
+  setAttribute("WM/Year", String::number(value));
+}
+
+void
+ASF::Tag::setTrack(uint value)
+{
+  setAttribute("WM/TrackNumber", String::number(value));
+}
+
+ASF::AttributeListMap&
+ASF::Tag::attributeListMap()
+{
+  return d->attributeListMap;
+}
+
+void ASF::Tag::removeItem(const String &key)
+{
+  AttributeListMap::Iterator it = d->attributeListMap.find(key);
+  if(it != d->attributeListMap.end())
+    d->attributeListMap.erase(it);
+}
+
+void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
+{
+  AttributeList value;
+  value.append(attribute);
+  d->attributeListMap.insert(name, value);
+}
+
+void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
+{
+  if(d->attributeListMap.contains(name)) {
+    d->attributeListMap[name].append(attribute);
+  }
+  else {
+    setAttribute(name, attribute);
+  }
+}
+
+bool ASF::Tag::isEmpty() const {
+  return TagLib::Tag::isEmpty() &&
+         copyright().isEmpty() &&
+         rating().isEmpty() &&
+         d->attributeListMap.isEmpty();
+}
+
+#endif
diff --git a/src/taglib/asf/asftag.h b/src/taglib/asf/asftag.h
new file mode 100644 (file)
index 0000000..8e73067
--- /dev/null
@@ -0,0 +1,186 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFTAG_H
+#define TAGLIB_ASFTAG_H
+
+#include "tag.h"
+#include "tlist.h"
+#include "tmap.h"
+#include "taglib_export.h"
+#include "asfattribute.h"
+
+namespace TagLib {
+
+  namespace ASF {
+
+    typedef List<Attribute> AttributeList;
+    typedef Map<String, AttributeList> AttributeListMap;
+
+    class TAGLIB_EXPORT Tag : public TagLib::Tag {
+
+      friend class File;
+
+    public:
+
+      Tag();
+
+      virtual ~Tag();
+
+      /*!
+       * Returns the track name.
+       */
+      virtual String title() const;
+
+      /*!
+       * Returns the artist name.
+       */
+      virtual String artist() const;
+
+      /*!
+       * Returns the album name; if no album name is present in the tag
+       * String::null will be returned.
+       */
+      virtual String album() const;
+
+      /*!
+       * Returns the track comment.
+       */
+      virtual String comment() const;
+
+      /*!
+       * Returns the genre name; if no genre is present in the tag String::null
+       * will be returned.
+       */
+      virtual String genre() const;
+
+      /*!
+       * Returns the rating.
+       */
+      virtual String rating() const;
+
+      /*!
+       * Returns the genre name; if no genre is present in the tag String::null
+       * will be returned.
+       */
+      virtual String copyright() const;
+
+      /*!
+       * Returns the year; if there is no year set, this will return 0.
+       */
+      virtual uint year() const;
+
+      /*!
+       * Returns the track number; if there is no track number set, this will
+       * return 0.
+       */
+      virtual uint track() const;
+
+      /*!
+       * Sets the title to \a s.
+       */
+      virtual void setTitle(const String &s);
+
+      /*!
+       * Sets the artist to \a s.
+       */
+      virtual void setArtist(const String &s);
+
+      /*!
+       * Sets the album to \a s.  If \a s is String::null then this value will be
+       * cleared.
+       */
+      virtual void setAlbum(const String &s);
+
+      /*!
+       * Sets the comment to \a s.
+       */
+      virtual void setComment(const String &s);
+
+      /*!
+       * Sets the rating to \a s. 
+       */
+      virtual void setRating(const String &s);
+
+      /*!
+       * Sets the copyright to \a s. 
+       */
+      virtual void setCopyright(const String &s);
+
+      /*!
+       * Sets the genre to \a s. 
+       */
+      virtual void setGenre(const String &s);
+
+      /*!
+       * Sets the year to \a i.  If \a s is 0 then this value will be cleared.
+       */
+      virtual void setYear(uint i);
+
+      /*!
+       * Sets the track to \a i.  If \a s is 0 then this value will be cleared.
+       */
+      virtual void setTrack(uint i);
+
+      /*!
+       * Returns true if the tag does not contain any data.  This should be
+       * reimplemented in subclasses that provide more than the basic tagging
+       * abilities in this class.
+       */
+      virtual bool isEmpty() const;
+
+      /*!
+       * Returns a reference to the item list map.  This is an AttributeListMap of
+       * all of the items in the tag.
+       *
+       * This is the most powerfull structure for accessing the items of the tag.
+       */
+      AttributeListMap &attributeListMap();
+
+      /*!
+       * Removes the \a key attribute from the tag
+       */
+      void removeItem(const String &name);
+
+      /*!
+       * Sets the \a key attribute to the value of \a attribute. If an attribute
+       * with the \a key is already present, it will be replaced.
+       */
+      void setAttribute(const String &name, const Attribute &attribute);
+
+      /*!
+       * Sets the \a key attribute to the value of \a attribute. If an attribute
+       * with the \a key is already present, it will be added to the list.
+       */
+      void addAttribute(const String &name, const Attribute &attribute);
+
+    private:
+
+      class TagPrivate;
+      TagPrivate *d;
+    };
+  }
+}
+#endif
diff --git a/src/taglib/audioproperties.cpp b/src/taglib/audioproperties.cpp
new file mode 100644 (file)
index 0000000..324cc35
--- /dev/null
@@ -0,0 +1,51 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "audioproperties.h"
+
+using namespace TagLib;
+
+class AudioProperties::AudioPropertiesPrivate
+{
+
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+AudioProperties::~AudioProperties()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+AudioProperties::AudioProperties(ReadStyle)
+{
+
+}
diff --git a/src/taglib/audioproperties.h b/src/taglib/audioproperties.h
new file mode 100644 (file)
index 0000000..4841031
--- /dev/null
@@ -0,0 +1,110 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_AUDIOPROPERTIES_H
+#define TAGLIB_AUDIOPROPERTIES_H
+
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  //! A simple, abstract interface to common audio properties
+
+  /*!
+   * The values here are common to most audio formats.  For more specific, codec
+   * dependant values, please see see the subclasses APIs.  This is meant to
+   * compliment the TagLib::File and TagLib::Tag APIs in providing a simple
+   * interface that is sufficient for most applications.
+   */
+
+  class TAGLIB_EXPORT AudioProperties
+  {
+  public:
+
+    /*!
+     * Reading audio properties from a file can sometimes be very time consuming
+     * and for the most accurate results can often involve reading the entire
+     * file.  Because in many situations speed is critical or the accuracy of the
+     * values is not particularly important this allows the level of desired
+     * accuracy to be set.
+     */
+    enum ReadStyle {
+      //! Read as little of the file as possible
+      Fast,
+      //! Read more of the file and make better values guesses
+      Average,
+      //! Read as much of the file as needed to report accurate values
+      Accurate
+    };
+
+    /*!
+     * Destroys this AudioProperties instance.
+     */
+    virtual ~AudioProperties();
+
+    /*!
+     * Returns the length of the file in seconds.
+     */
+    virtual int length() const = 0;
+
+    /*!
+     * Returns the most appropriate bit rate for the file in kb/s.  For constant
+     * bitrate formats this is simply the bitrate of the file.  For variable
+     * bitrate formats this is either the average or nominal bitrate.
+     */
+    virtual int bitrate() const = 0;
+
+    /*!
+     * Returns the sample rate in Hz.
+     */
+    virtual int sampleRate() const = 0;
+
+    /*!
+     * Returns the number of audio channels.
+     */
+    virtual int channels() const = 0;
+
+  protected:
+
+    /*!
+     * Construct an audio properties instance.  This is protected as this class
+     * should not be instantiated directly, but should be instantiated via its
+     * subclasses and can be fetched from the FileRef or File APIs.
+     *
+     * \see ReadStyle
+     */
+    AudioProperties(ReadStyle style);
+
+  private:
+    AudioProperties(const AudioProperties &);
+    AudioProperties &operator=(const AudioProperties &);
+
+    class AudioPropertiesPrivate;
+    AudioPropertiesPrivate *d;
+  };
+
+}
+
+#endif
diff --git a/src/taglib/fileref.cpp b/src/taglib/fileref.cpp
new file mode 100644 (file)
index 0000000..fec4516
--- /dev/null
@@ -0,0 +1,260 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <tfile.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "fileref.h"
+#include "asffile.h"
+#include "mpegfile.h"
+#include "vorbisfile.h"
+#include "flacfile.h"
+#include "oggflacfile.h"
+#include "mpcfile.h"
+#include "mp4file.h"
+#include "wavpackfile.h"
+#include "speexfile.h"
+#include "trueaudiofile.h"
+#include "aifffile.h"
+#include "wavfile.h"
+
+using namespace TagLib;
+
+class FileRef::FileRefPrivate : public RefCounter
+{
+public:
+  FileRefPrivate(File *f) : RefCounter(), file(f) {}
+  ~FileRefPrivate() {
+    delete file;
+  }
+
+  File *file;
+  static List<const FileTypeResolver *> fileTypeResolvers;
+};
+
+List<const FileRef::FileTypeResolver *> FileRef::FileRefPrivate::fileTypeResolvers;
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FileRef::FileRef()
+{
+  d = new FileRefPrivate(0);
+}
+
+FileRef::FileRef(FileName fileName, bool readAudioProperties,
+                 AudioProperties::ReadStyle audioPropertiesStyle)
+{
+  d = new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle));
+}
+
+FileRef::FileRef(File *file)
+{
+  d = new FileRefPrivate(file);
+}
+
+FileRef::FileRef(const FileRef &ref) : d(ref.d)
+{
+  d->ref();
+}
+
+FileRef::~FileRef()
+{
+  if(d->deref())
+    delete d;
+}
+
+Tag *FileRef::tag() const
+{
+  if(isNull()) {
+    debug("FileRef::tag() - Called without a valid file.");
+    return 0;
+  }
+  return d->file->tag();
+}
+
+AudioProperties *FileRef::audioProperties() const
+{
+  if(isNull()) {
+    debug("FileRef::audioProperties() - Called without a valid file.");
+    return 0;
+  }
+  return d->file->audioProperties();
+}
+
+File *FileRef::file() const
+{
+  return d->file;
+}
+
+bool FileRef::save()
+{
+  if(isNull()) {
+    debug("FileRef::save() - Called without a valid file.");
+    return false;
+  }
+  return d->file->save();
+}
+
+const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
+{
+  FileRefPrivate::fileTypeResolvers.prepend(resolver);
+  return resolver;
+}
+
+StringList FileRef::defaultFileExtensions()
+{
+  StringList l;
+
+  l.append("ogg");
+  l.append("flac");
+  l.append("oga");
+  l.append("mp3");
+  l.append("mpc");
+  l.append("wv");
+  l.append("spx");
+  l.append("tta");
+#ifdef TAGLIB_WITH_MP4
+  l.append("m4a");
+  l.append("m4b");
+  l.append("m4p");
+  l.append("3g2");
+  l.append("mp4");
+#endif
+#ifdef TAGLIB_WITH_ASF
+  l.append("wma");
+  l.append("asf");
+#endif
+  l.append("aif");
+  l.append("aiff");
+  l.append("wav");
+
+  return l;
+}
+
+bool FileRef::isNull() const
+{
+  return !d->file || !d->file->isValid();
+}
+
+FileRef &FileRef::operator=(const FileRef &ref)
+{
+  if(&ref == this)
+    return *this;
+
+  if(d->deref())
+    delete d;
+
+  d = ref.d;
+  d->ref();
+
+  return *this;
+}
+
+bool FileRef::operator==(const FileRef &ref) const
+{
+  return ref.d->file == d->file;
+}
+
+bool FileRef::operator!=(const FileRef &ref) const
+{
+  return ref.d->file != d->file;
+}
+
+File *FileRef::create(FileName fileName, bool readAudioProperties,
+                      AudioProperties::ReadStyle audioPropertiesStyle) // static
+{
+
+  List<const FileTypeResolver *>::ConstIterator it = FileRefPrivate::fileTypeResolvers.begin();
+
+  for(; it != FileRefPrivate::fileTypeResolvers.end(); ++it) {
+    File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
+    if(file)
+      return file;
+  }
+
+  // Ok, this is really dumb for now, but it works for testing.
+
+  String s;
+
+#ifdef _WIN32
+  s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName);
+#else
+  s = fileName;
+#endif
+
+  // If this list is updated, the method defaultFileExtensions() should also be
+  // updated.  However at some point that list should be created at the same time
+  // that a default file type resolver is created.
+
+  int pos = s.rfind(".");
+  if(pos != -1) {
+    String ext = s.substr(pos + 1).upper();
+    if(ext == "MP3")
+      return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "OGG")
+      return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "OGA") {
+      /* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
+      File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
+      if (file->isValid())
+        return file;
+      delete file;
+      return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
+    }
+    if(ext == "FLAC")
+      return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "MPC")
+      return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "WV")
+      return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "SPX")
+      return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "TTA")
+      return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
+#ifdef TAGLIB_WITH_MP4
+    if(ext == "M4A" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
+      return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
+#endif
+#ifdef TAGLIB_WITH_ASF
+    if(ext == "WMA" || ext == "ASF")
+      return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
+#endif
+    if(ext == "AIF")
+      return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "WAV")
+      return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(ext == "AIFF")
+      return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
+  }
+
+  return 0;
+}
diff --git a/src/taglib/fileref.h b/src/taglib/fileref.h
new file mode 100644 (file)
index 0000000..0cfe611
--- /dev/null
@@ -0,0 +1,260 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_FILEREF_H
+#define TAGLIB_FILEREF_H
+
+#include "tfile.h"
+#include "tstringlist.h"
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  class Tag;
+
+  //! This class provides a simple abstraction for creating and handling files
+
+  /*!
+   * FileRef exists to provide a minimal, generic and value-based wrapper around
+   * a File.  It is lightweight and implicitly shared, and as such suitable for
+   * pass-by-value use.  This hides some of the uglier details of TagLib::File
+   * and the non-generic portions of the concrete file implementations.
+   *
+   * This class is useful in a "simple usage" situation where it is desirable
+   * to be able to get and set some of the tag information that is similar
+   * across file types.
+   *
+   * Also note that it is probably a good idea to plug this into your mime
+   * type system rather than using the constructor that accepts a file name using
+   * the FileTypeResolver.
+   *
+   * \see FileTypeResolver
+   * \see addFileTypeResolver()
+   */
+
+  class TAGLIB_EXPORT FileRef
+  {
+  public:
+
+  //! A class for pluggable file type resolution.
+
+  /*!
+   * This class is used to add extend TagLib's very basic file name based file
+   * type resolution.
+   *
+   * This can be accomplished with:
+   *
+   * \code
+   *
+   * class MyFileTypeResolver : FileTypeResolver
+   * {
+   *   TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle)
+   *   {
+   *     if(someCheckForAnMP3File(fileName))
+   *       return new TagLib::MPEG::File(fileName);
+   *     return 0;
+   *   }
+   * }
+   *
+   * FileRef::addFileTypeResolver(new MyFileTypeResolver);
+   *
+   * \endcode
+   *
+   * Naturally a less contrived example would be slightly more complex.  This
+   * can be used to plug in mime-type detection systems or to add new file types
+   * to TagLib.
+   */
+
+    class TAGLIB_EXPORT FileTypeResolver
+    {
+      TAGLIB_IGNORE_MISSING_DESTRUCTOR
+    public:
+      /*!
+       * This method must be overridden to provide an additional file type
+       * resolver.  If the resolver is able to determine the file type it should
+       * return a valid File object; if not it should return 0.
+       *
+       * \note The created file is then owned by the FileRef and should not be
+       * deleted.  Deletion will happen automatically when the FileRef passes
+       * out of scope.
+       */
+      virtual File *createFile(FileName fileName,
+                               bool readAudioProperties = true,
+                               AudioProperties::ReadStyle
+                               audioPropertiesStyle = AudioProperties::Average) const = 0;
+    };
+
+    /*!
+     * Creates a null FileRef.
+     */
+    FileRef();
+
+    /*!
+     * Create a FileRef from \a fileName.  If \a readAudioProperties is true then
+     * the audio properties will be read using \a audioPropertiesStyle.  If
+     * \a readAudioProperties is false then \a audioPropertiesStyle will be
+     * ignored.
+     *
+     * Also see the note in the class documentation about why you may not want to
+     * use this method in your application.
+     */
+    explicit FileRef(FileName fileName,
+                     bool readAudioProperties = true,
+                     AudioProperties::ReadStyle
+                     audioPropertiesStyle = AudioProperties::Average);
+
+    /*!
+     * Contruct a FileRef using \a file.  The FileRef now takes ownership of the
+     * pointer and will delete the File when it passes out of scope.
+     */
+    explicit FileRef(File *file);
+
+    /*!
+     * Make a copy of \a ref.
+     */
+    FileRef(const FileRef &ref);
+
+    /*!
+     * Destroys this FileRef instance.
+     */
+    virtual ~FileRef();
+
+    /*!
+     * Returns a pointer to represented file's tag.
+     *
+     * \warning This pointer will become invalid when this FileRef and all
+     * copies pass out of scope.
+     *
+     * \see File::tag()
+     */
+    Tag *tag() const;
+
+    /*!
+     * Returns the audio properties for this FileRef.  If no audio properties
+     * were read then this will returns a null pointer.
+     */
+    AudioProperties *audioProperties() const;
+
+    /*!
+     * Returns a pointer to the file represented by this handler class.
+     *
+     * As a general rule this call should be avoided since if you need to work
+     * with file objects directly, you are probably better served instantiating
+     * the File subclasses (i.e. MPEG::File) manually and working with their APIs.
+     *
+     * This <i>handle</i> exists to provide a minimal, generic and value-based
+     * wrapper around a File.  Accessing the file directly generally indicates
+     * a moving away from this simplicity (and into things beyond the scope of
+     * FileRef).
+     *
+     * \warning This pointer will become invalid when this FileRef and all
+     * copies pass out of scope.
+     */
+    File *file() const;
+
+    /*!
+     * Saves the file.  Returns true on success.
+     */
+    bool save();
+
+    /*!
+     * Adds a FileTypeResolver to the list of those used by TagLib.  Each
+     * additional FileTypeResolver is added to the front of a list of resolvers
+     * that are tried.  If the FileTypeResolver returns zero the next resolver
+     * is tried.
+     *
+     * Returns a pointer to the added resolver (the same one that's passed in --
+     * this is mostly so that static inialializers have something to use for
+     * assignment).
+     *
+     * \see FileTypeResolver
+     */
+    static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver);
+
+    /*!
+     * As is mentioned elsewhere in this class's documentation, the default file
+     * type resolution code provided by TagLib only works by comparing file
+     * extensions.
+     *
+     * This method returns the list of file extensions that are used by default.
+     *
+     * The extensions are all returned in lowercase, though the comparison used
+     * by TagLib for resolution is case-insensitive.
+     *
+     * \note This does not account for any additional file type resolvers that
+     * are plugged in.  Also note that this is not intended to replace a propper
+     * mime-type resolution system, but is just here for reference.
+     *
+     * \see FileTypeResolver
+     */
+    static StringList defaultFileExtensions();
+
+    /*!
+     * Returns true if the file (and as such other pointers) are null.
+     */
+    bool isNull() const;
+
+    /*!
+     * Assign the file pointed to by \a ref to this FileRef.
+     */
+    FileRef &operator=(const FileRef &ref);
+
+    /*!
+     * Returns true if this FileRef and \a ref point to the same File object.
+     */
+    bool operator==(const FileRef &ref) const;
+
+    /*!
+     * Returns true if this FileRef and \a ref do not point to the same File
+     * object.
+     */
+    bool operator!=(const FileRef &ref) const;
+
+    /*!
+     * A simple implementation of file type guessing.  If \a readAudioProperties
+     * is true then the audio properties will be read using
+     * \a audioPropertiesStyle.  If \a readAudioProperties is false then
+     * \a audioPropertiesStyle will be ignored.
+     *
+     * \note You generally shouldn't use this method, but instead the constructor
+     * directly.
+     *
+     * \deprecated
+     */
+    static File *create(FileName fileName,
+                        bool readAudioProperties = true,
+                        AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
+
+
+  private:
+    class FileRefPrivate;
+    FileRefPrivate *d;
+  };
+
+} // namespace TagLib
+
+#endif
diff --git a/src/taglib/flac/flacfile.cpp b/src/taglib/flac/flacfile.cpp
new file mode 100644 (file)
index 0000000..7f3d902
--- /dev/null
@@ -0,0 +1,504 @@
+/***************************************************************************
+    copyright            : (C) 2003-2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tlist.h>
+#include <tdebug.h>
+#include <tagunion.h>
+
+#include <id3v2header.h>
+#include <id3v2tag.h>
+#include <id3v1tag.h>
+#include <xiphcomment.h>
+
+#include "flacfile.h"
+
+using namespace TagLib;
+
+namespace
+{
+  enum { XiphIndex = 0, ID3v2Index = 1, ID3v1Index = 2 };
+  enum { StreamInfo = 0, Padding, Application, SeekTable, VorbisComment, CueSheet };
+  enum { MinPaddingLength = 4096 };
+}
+
+class FLAC::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
+    ID3v2Location(-1),
+    ID3v2OriginalSize(0),
+    ID3v1Location(-1),
+    properties(0),
+    flacStart(0),
+    streamStart(0),
+    streamLength(0),
+    scanned(false),
+    hasXiphComment(false),
+    hasID3v2(false),
+    hasID3v1(false) {}
+
+  ~FilePrivate()
+  {
+    delete properties;
+  }
+
+  const ID3v2::FrameFactory *ID3v2FrameFactory;
+  long ID3v2Location;
+  uint ID3v2OriginalSize;
+
+  long ID3v1Location;
+
+  TagUnion tag;
+
+  Properties *properties;
+  ByteVector streamInfoData;
+  ByteVector xiphCommentData;
+
+  long flacStart;
+  long streamStart;
+  long streamLength;
+  bool scanned;
+
+  bool hasXiphComment;
+  bool hasID3v2;
+  bool hasID3v1;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FLAC::File::File(FileName file, bool readProperties,
+                 Properties::ReadStyle propertiesStyle) :
+  TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
+                 bool readProperties, Properties::ReadStyle propertiesStyle) :
+  TagLib::File(file)
+{
+  d = new FilePrivate;
+  d->ID3v2FrameFactory = frameFactory;
+  read(readProperties, propertiesStyle);
+}
+
+FLAC::File::~File()
+{
+  delete d;
+}
+
+TagLib::Tag *FLAC::File::tag() const
+{
+  return &d->tag;
+}
+
+FLAC::Properties *FLAC::File::audioProperties() const
+{
+  return d->properties;
+}
+
+
+bool FLAC::File::save()
+{
+  if(readOnly()) {
+    debug("FLAC::File::save() - Cannot save to a read only file.");
+    return false;
+  }
+
+  // Create new vorbis comments
+
+  Tag::duplicate(&d->tag, xiphComment(true), true);
+
+  d->xiphCommentData = xiphComment()->render(false);
+
+  // A Xiph comment portion of the data stream starts with a 4-byte descriptor.
+  // The first byte indicates the frame type.  The last three bytes are used
+  // to give the length of the data segment.  Here we start
+
+  ByteVector data = ByteVector::fromUInt(d->xiphCommentData.size());
+
+  data[0] = char(VorbisComment);
+  data.append(d->xiphCommentData);
+
+
+   // If file already have comment => find and update it
+   // if not => insert one
+
+   // TODO: Search for padding and use that
+
+  if(d->hasXiphComment) {
+
+    long nextBlockOffset = d->flacStart;
+    bool isLastBlock = false;
+
+    while(!isLastBlock) {
+      seek(nextBlockOffset);
+
+      ByteVector header = readBlock(4);
+      char blockType = header[0] & 0x7f;
+      isLastBlock = (header[0] & 0x80) != 0;
+      uint blockLength = header.mid(1, 3).toUInt();
+
+      if(blockType == VorbisComment) {
+
+        long paddingBreak = 0;
+
+        if(!isLastBlock) {
+          paddingBreak = findPaddingBreak(nextBlockOffset + blockLength + 4,
+                                          nextBlockOffset + d->xiphCommentData.size() + 8,
+                                          &isLastBlock);
+        }
+
+        uint paddingLength = 0;
+
+         if(paddingBreak) {
+
+           // There is space for comment and padding blocks without rewriting the
+           // whole file.  Note: This cannot overflow.
+
+           paddingLength = paddingBreak - (nextBlockOffset + d->xiphCommentData.size() + 8);
+         }
+         else {
+
+           // Not enough space, so we will have to rewrite the whole file
+           // following this block
+
+           paddingLength = d->xiphCommentData.size();
+
+           if(paddingLength < MinPaddingLength)
+             paddingLength = MinPaddingLength;
+
+           paddingBreak = nextBlockOffset + blockLength + 4;
+         }
+
+         ByteVector padding = ByteVector::fromUInt(paddingLength);
+
+         padding[0] = 1;
+
+         if(isLastBlock)
+           padding[0] |= 0x80;
+
+         padding.resize(paddingLength + 4);
+         ByteVector pair(data);
+         pair.append(padding);
+         insert(pair, nextBlockOffset, paddingBreak - nextBlockOffset);
+         break;
+      }
+
+      nextBlockOffset += blockLength + 4;
+    }
+  }
+  else {
+
+    const long firstBlockOffset = d->flacStart;
+    seek(firstBlockOffset);
+
+    ByteVector header = readBlock(4);
+    bool isLastBlock = (header[0] & 0x80) != 0;
+    uint blockLength = header.mid(1, 3).toUInt();
+
+    if(isLastBlock) {
+
+      // If the first block was previously also the last block, then we want to
+      // mark it as no longer being the first block (the writeBlock() call) and
+      // then set the data for the block that we're about to write to mark our
+      // new block as the last block.
+
+      seek(firstBlockOffset);
+      writeBlock(static_cast<char>(header[0] & 0x7F));
+      data[0] |= 0x80;
+    }
+
+    insert(data, firstBlockOffset + blockLength + 4, 0);
+    d->hasXiphComment = true;
+  }
+
+  // Update ID3 tags
+
+  if(ID3v2Tag()) {
+    if(d->hasID3v2) {
+      if(d->ID3v2Location < d->flacStart)
+        debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
+              "start of the FLAC bytestream?  Not writing the ID3v2 tag.");
+      else
+        insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
+    }
+    else
+      insert(ID3v2Tag()->render(), 0, 0);
+  }
+
+  if(ID3v1Tag()) {
+    seek(-128, End);
+    writeBlock(ID3v1Tag()->render());
+  }
+
+  return true;
+}
+
+ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
+{
+  if(!create || d->tag[ID3v2Index])
+    return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
+
+  d->tag.set(ID3v2Index, new ID3v2::Tag);
+  return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
+}
+
+ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
+{
+  return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
+}
+
+Ogg::XiphComment *FLAC::File::xiphComment(bool create)
+{
+  return d->tag.access<Ogg::XiphComment>(XiphIndex, create);
+}
+
+void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
+{
+  d->ID3v2FrameFactory = factory;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  // Look for an ID3v2 tag
+
+  d->ID3v2Location = findID3v2();
+
+  if(d->ID3v2Location >= 0) {
+
+    d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
+
+    d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
+
+    if(ID3v2Tag()->header()->tagSize() <= 0)
+      d->tag.set(ID3v2Index, 0);
+    else
+      d->hasID3v2 = true;
+  }
+
+  // Look for an ID3v1 tag
+
+  d->ID3v1Location = findID3v1();
+
+  if(d->ID3v1Location >= 0) {
+    d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
+    d->hasID3v1 = true;
+  }
+
+  // Look for FLAC metadata, including vorbis comments
+
+  scan();
+
+  if(!isValid())
+    return;
+
+  if(d->hasXiphComment)
+    d->tag.set(XiphIndex, new Ogg::XiphComment(xiphCommentData()));
+  else
+    d->tag.set(XiphIndex, new Ogg::XiphComment);
+
+  if(readProperties)
+    d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
+}
+
+ByteVector FLAC::File::streamInfoData()
+{
+  return isValid() ? d->streamInfoData : ByteVector();
+}
+
+ByteVector FLAC::File::xiphCommentData() const
+{
+  return (isValid() && d->hasXiphComment) ? d->xiphCommentData : ByteVector();
+}
+
+long FLAC::File::streamLength()
+{
+  return d->streamLength;
+}
+
+void FLAC::File::scan()
+{
+  // Scan the metadata pages
+
+  if(d->scanned)
+    return;
+
+  if(!isValid())
+    return;
+
+  long nextBlockOffset;
+
+  if(d->hasID3v2)
+    nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
+  else
+    nextBlockOffset = find("fLaC");
+
+  if(nextBlockOffset < 0) {
+    debug("FLAC::File::scan() -- FLAC stream not found");
+    setValid(false);
+    return;
+  }
+
+  nextBlockOffset += 4;
+  d->flacStart = nextBlockOffset;
+
+  seek(nextBlockOffset);
+
+  ByteVector header = readBlock(4);
+
+  // Header format (from spec):
+  // <1> Last-metadata-block flag
+  // <7> BLOCK_TYPE
+  //    0 : STREAMINFO
+  //    1 : PADDING
+  //    ..
+  //    4 : VORBIS_COMMENT
+  //    ..
+  // <24> Length of metadata to follow
+
+  char blockType = header[0] & 0x7f;
+  bool isLastBlock = (header[0] & 0x80) != 0;
+  uint length = header.mid(1, 3).toUInt();
+
+  // First block should be the stream_info metadata
+
+  if(blockType != StreamInfo) {
+    debug("FLAC::File::scan() -- invalid FLAC stream");
+    setValid(false);
+    return;
+  }
+
+  d->streamInfoData = readBlock(length);
+  nextBlockOffset += length + 4;
+
+  // Search through the remaining metadata
+  while(!isLastBlock) {
+
+    header = readBlock(4);
+    blockType = header[0] & 0x7f;
+    isLastBlock = (header[0] & 0x80) != 0;
+    length = header.mid(1, 3).toUInt();
+
+    // Found the vorbis-comment
+    if(blockType == VorbisComment) {
+      if(!d->hasXiphComment) {
+        d->xiphCommentData = readBlock(length);
+        d->hasXiphComment = true;
+      }
+      else {
+        debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one");
+      }
+    }
+
+    nextBlockOffset += length + 4;
+
+    if(nextBlockOffset >= File::length()) {
+      debug("FLAC::File::scan() -- FLAC stream corrupted");
+      setValid(false);
+      return;
+    }
+    seek(nextBlockOffset);
+  }
+
+  // End of metadata, now comes the datastream
+
+  d->streamStart = nextBlockOffset;
+  d->streamLength = File::length() - d->streamStart;
+
+  if(d->hasID3v1)
+    d->streamLength -= 128;
+
+  d->scanned = true;
+}
+
+long FLAC::File::findID3v1()
+{
+  if(!isValid())
+    return -1;
+
+  seek(-128, End);
+  long p = tell();
+
+  if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+    return p;
+
+  return -1;
+}
+
+long FLAC::File::findID3v2()
+{
+  if(!isValid())
+    return -1;
+
+  seek(0);
+
+  if(readBlock(3) == ID3v2::Header::fileIdentifier())
+    return 0;
+
+  return -1;
+}
+
+long FLAC::File::findPaddingBreak(long nextBlockOffset, long targetOffset, bool *isLast)
+{
+  // Starting from nextBlockOffset, step over padding blocks to find the
+  // address of a block which is after targetOffset. Return zero if
+  // a non-padding block occurs before that point.
+
+  while(true) {
+    seek(nextBlockOffset);
+
+    ByteVector header = readBlock(4);
+    char blockType = header[0] & 0x7f;
+    bool isLastBlock = header[0] & 0x80;
+    uint length = header.mid(1, 3).toUInt();
+
+    if(blockType != Padding)
+      break;
+
+    nextBlockOffset += 4 + length;
+
+    if(nextBlockOffset >= targetOffset) {
+      *isLast = isLastBlock;
+      return nextBlockOffset;
+    }
+
+    if(isLastBlock)
+      break;
+  }
+
+  return 0;
+}
diff --git a/src/taglib/flac/flacfile.h b/src/taglib/flac/flacfile.h
new file mode 100644 (file)
index 0000000..015ecc8
--- /dev/null
@@ -0,0 +1,202 @@
+/***************************************************************************
+    copyright            : (C) 2003 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_FLACFILE_H
+#define TAGLIB_FLACFILE_H
+
+#include "taglib_export.h"
+#include "tfile.h"
+
+#include "flacproperties.h"
+
+namespace TagLib {
+
+  class Tag;
+
+  namespace ID3v2 { class FrameFactory; class Tag; }
+  namespace ID3v1 { class Tag; }
+  namespace Ogg { class XiphComment; }
+
+  //! An implementation of FLAC metadata
+
+  /*!
+   * This is implementation of FLAC metadata for non-Ogg FLAC files.  At some
+   * point when Ogg / FLAC is more common there will be a similar implementation
+   * under the Ogg hiearchy.
+   *
+   * This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream
+   * properties from the file.
+   */
+
+  namespace FLAC {
+
+    //! An implementation of TagLib::File with FLAC specific methods
+
+    /*!
+     * This implements and provides an interface for FLAC files to the
+     * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+     * the abstract TagLib::File API as well as providing some additional
+     * information specific to FLAC files.
+     */
+
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * Contructs a FLAC file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * \deprecated This constructor will be dropped in favor of the one below
+       * in a future version.
+       */
+      File(FileName file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Contructs a FLAC file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * If this file contains and ID3v2 tag the frames will be created using
+       * \a frameFactory.
+       */
+      // BIC: merge with the above constructor
+      File(FileName file, ID3v2::FrameFactory *frameFactory,
+           bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns the Tag for this file.  This will be a union of XiphComment,
+       * ID3v1 and ID3v2 tags.
+       *
+       * \see ID3v2Tag()
+       * \see ID3v1Tag()
+       * \see XiphComment()
+       */
+      virtual TagLib::Tag *tag() const;
+
+      /*!
+       * Returns the FLAC::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Save the file.  This will primarily save the XiphComment, but
+       * will also keep any old ID3-tags up to date. If the file
+       * has no XiphComment, one will be constructed from the ID3-tags.
+       *
+       * This returns true if the save was successful.
+       */
+      virtual bool save();
+
+      /*!
+       * Returns a pointer to the ID3v2 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v2 tag.  If \a create is true it will create
+       * an ID3v2 tag if one does not exist.
+       *
+       * \note The Tag <b>is still</b> owned by the FLAC::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v2::Tag *ID3v2Tag(bool create = false);
+
+      /*!
+       * Returns a pointer to the ID3v1 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v1 tag.  If \a create is true it will create
+       * an ID3v1 tag if one does not exist.
+       *
+       * \note The Tag <b>is still</b> owned by the FLAC::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v1::Tag *ID3v1Tag(bool create = false);
+
+      /*!
+       * Returns a pointer to the XiphComment for the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid XiphComment.  If \a create is true it will create
+       * a XiphComment if one does not exist.
+       *
+       * \note The Tag <b>is still</b> owned by the FLAC::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      Ogg::XiphComment *xiphComment(bool create = false);
+
+      /*!
+       * Set the ID3v2::FrameFactory to something other than the default.  This
+       * can be used to specify the way that ID3v2 frames will be interpreted
+       * when
+       *
+       * \see ID3v2FrameFactory
+       */
+      void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
+
+      /*!
+       * Returns the block of data used by FLAC::Properties for parsing the
+       * stream properties.
+       *
+       * \deprecated This method will not be public in a future release.
+       */
+      ByteVector streamInfoData(); // BIC: remove
+
+      /*!
+       * Returns the length of the audio-stream, used by FLAC::Properties for
+       * calculating the bitrate.
+       *
+       * \deprecated This method will not be public in a future release.
+       */
+      long streamLength();  // BIC: remove
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+      void scan();
+      long findID3v2();
+      long findID3v1();
+      ByteVector xiphCommentData() const;
+      long findPaddingBreak(long nextPageOffset, long targetOffset, bool *isLast);
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/flac/flacproperties.cpp b/src/taglib/flac/flacproperties.cpp
new file mode 100644 (file)
index 0000000..f137059
--- /dev/null
@@ -0,0 +1,150 @@
+/***************************************************************************
+    copyright            : (C) 2003 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "flacproperties.h"
+#include "flacfile.h"
+
+using namespace TagLib;
+
+class FLAC::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(ByteVector d, long st, ReadStyle s) :
+    data(d),
+    streamLength(st),
+    style(s),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    sampleWidth(0),
+    channels(0) {}
+
+  ByteVector data;
+  long streamLength;
+  ReadStyle style;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int sampleWidth;
+  int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(data, streamLength, style);
+  read();
+}
+
+FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(file->streamInfoData(), file->streamLength(), style);
+  read();
+}
+
+FLAC::Properties::~Properties()
+{
+  delete d;
+}
+
+int FLAC::Properties::length() const
+{
+  return d->length;
+}
+
+int FLAC::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int FLAC::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int FLAC::Properties::sampleWidth() const
+{
+  return d->sampleWidth;
+}
+
+int FLAC::Properties::channels() const
+{
+  return d->channels;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FLAC::Properties::read()
+{
+  if(d->data.size() < 18) {
+    debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
+    return;
+  }
+
+  int pos = 0;
+
+  // Minimum block size (in samples)
+  pos += 2;
+
+  // Maximum block size (in samples)
+  pos += 2;
+
+  // Minimum frame size (in bytes)
+  pos += 3;
+
+  // Maximum frame size (in bytes)
+  pos += 3;
+
+  uint flags = d->data.mid(pos, 4).toUInt(true);
+  d->sampleRate = flags >> 12;
+  d->channels = ((flags >> 9) & 7) + 1;
+  d->sampleWidth = ((flags >> 4) & 31) + 1;
+
+  // The last 4 bits are the most significant 4 bits for the 36 bit
+  // stream length in samples. (Audio files measured in days)
+
+  uint highLength =d->sampleRate > 0 ? (((flags & 0xf) << 28) / d->sampleRate) << 4 : 0;
+  pos += 4;
+
+  d->length = d->sampleRate > 0 ?
+      (d->data.mid(pos, 4).toUInt(true)) / d->sampleRate + highLength : 0;
+  pos += 4;
+
+  // Uncompressed bitrate:
+
+  //d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth;
+
+  // Real bitrate:
+
+  d->bitrate = d->length > 0 ? ((d->streamLength * 8UL) / d->length) / 1000 : 0;
+}
diff --git a/src/taglib/flac/flacproperties.h b/src/taglib/flac/flacproperties.h
new file mode 100644 (file)
index 0000000..9ac6766
--- /dev/null
@@ -0,0 +1,92 @@
+/***************************************************************************
+    copyright            : (C) 2003 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_FLACPROPERTIES_H
+#define TAGLIB_FLACPROPERTIES_H
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace FLAC {
+
+    class File;
+
+    //! An implementation of audio property reading for FLAC
+
+    /*!
+     * This reads the data from an FLAC stream found in the AudioProperties
+     * API.
+     */
+
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      /*!
+       * Create an instance of FLAC::Properties with the data read from the
+       * ByteVector \a data.
+       */
+       // BIC: switch to const reference
+      Properties(ByteVector data, long streamLength, ReadStyle style = Average);
+
+      /*!
+       * Create an instance of FLAC::Properties with the data read from the
+       * FLAC::File \a file.
+       */
+       // BIC: remove
+      Properties(File *file, ReadStyle style = Average);
+
+      /*!
+       * Destroys this FLAC::Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+      /*!
+       * Returns the sample width as read from the FLAC identification
+       * header.
+       */
+      int sampleWidth() const;
+
+    private:
+      Properties(const Properties &);
+      Properties &operator=(const Properties &);
+
+      void read();
+
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4atom.cpp b/src/taglib/mp4/mp4atom.cpp
new file mode 100644 (file)
index 0000000..6d86a6e
--- /dev/null
@@ -0,0 +1,197 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4atom.h"
+
+using namespace TagLib;
+
+const char *MP4::Atom::containers[10] = {
+    "moov", "udta", "mdia", "meta", "ilst",
+    "stbl", "minf", "moof", "traf", "trak",
+};
+
+MP4::Atom::Atom(File *file)
+{
+  offset = file->tell();
+  ByteVector header = file->readBlock(8);
+  if (header.size() != 8) {
+    // The atom header must be 8 bytes long, otherwise there is either
+    // trailing garbage or the file is truncated
+    debug("MP4: Couldn't read 8 bytes of data for atom header");
+    length = 0;
+    file->seek(0, File::End);
+    return;
+  }
+
+  length = header.mid(0, 4).toUInt();
+
+  if (length == 1) {
+    long long longLength = file->readBlock(8).toLongLong();
+    if (longLength >= 8 && longLength <= 0xFFFFFFFF) {
+        // The atom has a 64-bit length, but it's actually a 32-bit value
+        length = (long)longLength;
+    }
+    else {
+        debug("MP4: 64-bit atoms are not supported");
+        length = 0;
+        file->seek(0, File::End);
+        return;
+    }
+  }
+  if (length < 8) {
+    debug("MP4: Invalid atom size");
+    length = 0;
+    file->seek(0, File::End);
+    return;
+  }
+
+  name = header.mid(4, 4);
+
+  for(int i = 0; i < numContainers; i++) {
+    if(name == containers[i]) {
+      if(name == "meta") {
+        file->seek(4, File::Current);
+      }
+      while(file->tell() < offset + length) {
+        MP4::Atom *child = new MP4::Atom(file);
+        children.append(child);
+        if (child->length == 0)
+          return;
+      }
+      return;
+    }
+  }
+
+  file->seek(offset + length);
+}
+
+MP4::Atom::~Atom()
+{
+  for(unsigned int i = 0; i < children.size(); i++) {
+    delete children[i];
+  }
+  children.clear();
+}
+
+MP4::Atom *
+MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4)
+{
+  if(name1 == 0) {
+    return this;
+  }
+  for(unsigned int i = 0; i < children.size(); i++) {
+    if(children[i]->name == name1) {
+      return children[i]->find(name2, name3, name4);
+    }
+  }
+  return 0;
+}
+
+MP4::AtomList
+MP4::Atom::findall(const char *name, bool recursive)
+{
+  MP4::AtomList result;
+  for(unsigned int i = 0; i < children.size(); i++) {
+    if(children[i]->name == name) {
+      result.append(children[i]);
+    }
+    if(recursive) {
+      result.append(children[i]->findall(name, recursive));
+    }
+  }
+  return result;
+}
+
+bool
+MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3)
+{
+  path.append(this);
+  if(name1 == 0) {
+    return true;
+  }
+  for(unsigned int i = 0; i < children.size(); i++) {
+    if(children[i]->name == name1) {
+      return children[i]->path(path, name2, name3);
+    }
+  }
+  return false;
+}
+
+MP4::Atoms::Atoms(File *file)
+{
+  file->seek(0, File::End);
+  long end = file->tell();
+  file->seek(0);
+  while(file->tell() + 8 <= end) {
+    MP4::Atom *atom = new MP4::Atom(file);
+    atoms.append(atom);
+    if (atom->length == 0)
+      break;
+  }
+}
+
+MP4::Atoms::~Atoms()
+{
+  for(unsigned int i = 0; i < atoms.size(); i++) {
+    delete atoms[i];
+  }
+  atoms.clear();
+}
+
+MP4::Atom *
+MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
+{
+  for(unsigned int i = 0; i < atoms.size(); i++) {
+    if(atoms[i]->name == name1) {
+      return atoms[i]->find(name2, name3, name4);
+    }
+  }
+  return 0;
+}
+
+MP4::AtomList
+MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
+{
+  MP4::AtomList path;
+  for(unsigned int i = 0; i < atoms.size(); i++) {
+    if(atoms[i]->name == name1) {
+      if(!atoms[i]->path(path, name2, name3, name4)) {
+        path.clear();
+      }
+      return path;
+    }
+  }
+  return path;
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4atom.h b/src/taglib/mp4/mp4atom.h
new file mode 100644 (file)
index 0000000..a918561
--- /dev/null
@@ -0,0 +1,77 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+// This file is not part of the public API!
+
+#ifndef DO_NOT_DOCUMENT
+
+#ifndef TAGLIB_MP4ATOM_H
+#define TAGLIB_MP4ATOM_H
+
+#include "tfile.h"
+#include "tlist.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    class Atom;
+    typedef TagLib::List<Atom *> AtomList;
+
+    class Atom
+    {
+    public:
+        Atom(File *file);
+        ~Atom();
+        Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
+        bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
+        AtomList findall(const char *name, bool recursive = false);
+        long offset;
+        long length;
+        TagLib::ByteVector name;
+        AtomList children;
+    private:
+        static const int numContainers = 10;
+        static const char *containers[10];
+    };
+
+    //! Root-level atoms
+    class Atoms
+    {
+    public:
+        Atoms(File *file);
+        ~Atoms();
+        Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
+        AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
+        AtomList atoms;
+    };
+
+  }
+
+}
+
+#endif
+
+#endif
diff --git a/src/taglib/mp4/mp4coverart.cpp b/src/taglib/mp4/mp4coverart.cpp
new file mode 100644 (file)
index 0000000..983df02
--- /dev/null
@@ -0,0 +1,89 @@
+/**************************************************************************
+    copyright            : (C) 2009 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <taglib.h>
+#include <tdebug.h>
+#include "mp4coverart.h"
+
+using namespace TagLib;
+
+class MP4::CoverArt::CoverArtPrivate : public RefCounter
+{
+public:
+  CoverArtPrivate() : RefCounter(), format(MP4::CoverArt::JPEG) {}
+
+  Format format;
+  ByteVector data;
+};
+
+MP4::CoverArt::CoverArt(Format format, const ByteVector &data)
+{
+  d = new CoverArtPrivate;
+  d->format = format;
+  d->data = data;
+}
+
+MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d)
+{
+  d->ref();
+}
+
+MP4::CoverArt &
+MP4::CoverArt::operator=(const CoverArt &item)
+{
+  if(d->deref()) {
+    delete d;
+  }
+  d = item.d;
+  d->ref();
+  return *this;
+}
+
+MP4::CoverArt::~CoverArt()
+{
+  if(d->deref()) {
+    delete d;
+  }
+}
+
+MP4::CoverArt::Format
+MP4::CoverArt::format() const
+{
+  return d->format;
+}
+
+ByteVector
+MP4::CoverArt::data() const
+{
+  return d->data;
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4coverart.h b/src/taglib/mp4/mp4coverart.h
new file mode 100644 (file)
index 0000000..00a7aff
--- /dev/null
@@ -0,0 +1,71 @@
+/**************************************************************************
+    copyright            : (C) 2009 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4COVERART_H
+#define TAGLIB_MP4COVERART_H
+
+#include "tlist.h"
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    class TAGLIB_EXPORT CoverArt
+    {
+    public:
+      /*!
+       * This describes the image type.
+       */
+      enum Format {
+        JPEG = 0x0D,
+        PNG  = 0x0E
+      };
+
+      CoverArt(Format format, const ByteVector &data);
+      ~CoverArt();
+
+      CoverArt(const CoverArt &item);
+      CoverArt &operator=(const CoverArt &item);
+
+      //! Format of the image
+      Format format() const;
+
+      //! The image data
+      ByteVector data() const;
+
+    private:
+      class CoverArtPrivate;
+      CoverArtPrivate *d;
+    };
+
+    typedef List<CoverArt> CoverArtList;
+
+  }
+
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4file.cpp b/src/taglib/mp4/mp4file.cpp
new file mode 100644 (file)
index 0000000..21a5429
--- /dev/null
@@ -0,0 +1,138 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4atom.h"
+#include "mp4tag.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::File::FilePrivate
+{
+public:
+  FilePrivate() : tag(0), atoms(0), properties(0)
+  {
+  }
+
+  ~FilePrivate()
+  {
+    if(atoms) {
+        delete atoms;
+        atoms = 0;
+    }
+    if(tag) {
+        delete tag;
+        tag = 0;
+    }
+    if(properties) {
+        delete properties;
+        properties = 0;
+    }
+  }
+
+  MP4::Tag *tag;
+  MP4::Atoms *atoms;
+  MP4::Properties *properties;
+};
+
+MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
+    : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, audioPropertiesStyle);
+}
+
+MP4::File::~File()
+{
+  delete d;
+}
+
+MP4::Tag *
+MP4::File::tag() const
+{
+  return d->tag;
+}
+
+MP4::Properties *
+MP4::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool
+MP4::File::checkValid(const MP4::AtomList &list)
+{
+  for(uint i = 0; i < list.size(); i++) {
+    if(list[i]->length == 0)
+      return false;
+    if(!checkValid(list[i]->children))
+      return false;
+  }
+  return true;
+}
+
+void
+MP4::File::read(bool readProperties, Properties::ReadStyle audioPropertiesStyle)
+{
+  if(!isValid())
+    return;
+
+  d->atoms = new Atoms(this);
+  if (!checkValid(d->atoms->atoms)) {
+    setValid(false);
+    return;
+  }
+
+  // must have a moov atom, otherwise consider it invalid
+  MP4::Atom *moov = d->atoms->find("moov");
+  if(!moov) {
+    setValid(false);
+    return;
+  }
+
+  d->tag = new Tag(this, d->atoms);
+  if(readProperties) {
+    d->properties = new Properties(this, d->atoms, audioPropertiesStyle);
+  }
+}
+
+bool
+MP4::File::save()
+{
+  if(!isValid())
+    return false;
+
+  return d->tag->save();
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4file.h b/src/taglib/mp4/mp4file.h
new file mode 100644 (file)
index 0000000..333551a
--- /dev/null
@@ -0,0 +1,103 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4FILE_H
+#define TAGLIB_MP4FILE_H
+
+#include "tag.h"
+#include "tfile.h"
+#include "taglib_export.h"
+#include "mp4properties.h"
+#include "mp4tag.h"
+
+namespace TagLib {
+
+  //! An implementation of MP4 (AAC, ALAC, ...) metadata
+  namespace MP4 {
+
+    class Atoms;
+
+    /*!
+     * This implements and provides an interface for MP4 files to the
+     * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+     * the abstract TagLib::File API as well as providing some additional
+     * information specific to MP4 files.
+     */
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * Contructs a MP4 file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * \note In the current implementation, both \a readProperties and
+       * \a propertiesStyle are ignored.
+       */
+      File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns a pointer to the MP4 tag of the file.
+       *
+       * MP4::Tag implements the tag interface, so this serves as the
+       * reimplementation of TagLib::File::tag().
+       *
+       * \note The Tag <b>is still</b> owned by the MP4::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      Tag *tag() const;
+
+      /*!
+       * Returns the MP4 audio properties for this file.
+       */
+      Properties *audioProperties() const;
+
+      /*!
+       * Save the file.
+       *
+       * This returns true if the save was successful.
+       */
+      bool save();
+
+    private:
+
+      void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle);
+      bool checkValid(const MP4::AtomList &list);
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4item.cpp b/src/taglib/mp4/mp4item.cpp
new file mode 100644 (file)
index 0000000..0af331f
--- /dev/null
@@ -0,0 +1,149 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <taglib.h>
+#include <tdebug.h>
+#include "mp4item.h"
+
+using namespace TagLib;
+
+class MP4::Item::ItemPrivate : public RefCounter
+{
+public:
+  ItemPrivate() : RefCounter(), valid(true) {}
+
+  bool valid;
+  union {
+    bool m_bool;
+    int m_int;
+    IntPair m_intPair;
+  };
+  StringList m_stringList;
+  MP4::CoverArtList m_coverArtList;
+};
+
+MP4::Item::Item()
+{
+  d = new ItemPrivate;
+  d->valid = false;
+}
+
+MP4::Item::Item(const Item &item) : d(item.d)
+{
+  d->ref();
+}
+
+MP4::Item &
+MP4::Item::operator=(const Item &item)
+{
+  if(d->deref()) {
+    delete d;
+  }
+  d = item.d;
+  d->ref();
+  return *this;
+}
+
+MP4::Item::~Item()
+{
+  if(d->deref()) {
+    delete d;
+  }
+}
+
+MP4::Item::Item(bool value)
+{
+  d = new ItemPrivate;
+  d->m_bool = value;
+}
+
+MP4::Item::Item(int value)
+{
+  d = new ItemPrivate;
+  d->m_int = value;
+}
+
+MP4::Item::Item(int value1, int value2)
+{
+  d = new ItemPrivate;
+  d->m_intPair.first = value1;
+  d->m_intPair.second = value2;
+}
+
+MP4::Item::Item(const StringList &value)
+{
+  d = new ItemPrivate;
+  d->m_stringList = value;
+}
+
+MP4::Item::Item(const MP4::CoverArtList &value)
+{
+  d = new ItemPrivate;
+  d->m_coverArtList = value;
+}
+
+bool
+MP4::Item::toBool() const
+{
+  return d->m_bool;
+}
+
+int
+MP4::Item::toInt() const
+{
+  return d->m_int;
+}
+
+MP4::Item::IntPair
+MP4::Item::toIntPair() const
+{
+  return d->m_intPair;
+}
+
+StringList
+MP4::Item::toStringList() const
+{
+  return d->m_stringList;
+}
+
+MP4::CoverArtList
+MP4::Item::toCoverArtList() const
+{
+  return d->m_coverArtList;
+}
+
+bool
+MP4::Item::isValid() const
+{
+  return d->valid;
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4item.h b/src/taglib/mp4/mp4item.h
new file mode 100644 (file)
index 0000000..50a025f
--- /dev/null
@@ -0,0 +1,72 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4ITEM_H
+#define TAGLIB_MP4ITEM_H
+
+#include "tstringlist.h"
+#include "mp4coverart.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    class TAGLIB_EXPORT Item
+    {
+    public:
+      struct IntPair {
+        int first, second;
+      };
+
+      Item();
+      Item(const Item &item);
+      Item &operator=(const Item &item);
+      ~Item();
+
+      Item(int value);
+      Item(bool value);
+      Item(int first, int second);
+      Item(const StringList &value);
+      Item(const CoverArtList &value);
+
+      int toInt() const;
+      bool toBool() const;
+      IntPair toIntPair() const;
+      StringList toStringList() const;
+      CoverArtList toCoverArtList() const;
+
+      bool isValid() const;
+
+    private:
+      class ItemPrivate;
+      ItemPrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4properties.cpp b/src/taglib/mp4/mp4properties.cpp
new file mode 100644 (file)
index 0000000..c973d3a
--- /dev/null
@@ -0,0 +1,169 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4file.h"
+#include "mp4atom.h"
+#include "mp4properties.h"
+
+using namespace TagLib;
+
+class MP4::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0) {}
+
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+  int bitsPerSample;
+};
+
+MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
+  : AudioProperties(style)
+{
+  d = new PropertiesPrivate;
+
+  MP4::Atom *moov = atoms->find("moov");
+  if(!moov) {
+    debug("MP4: Atom 'moov' not found");
+    return;
+  }
+
+  MP4::Atom *trak = 0;
+  ByteVector data;
+
+  MP4::AtomList trakList = moov->findall("trak");
+  for (unsigned int i = 0; i < trakList.size(); i++) {
+    trak = trakList[i];
+    MP4::Atom *hdlr = trak->find("mdia", "hdlr");
+    if(!hdlr) {
+      debug("MP4: Atom 'trak.mdia.hdlr' not found");
+      return;
+    }
+    file->seek(hdlr->offset);
+    data = file->readBlock(hdlr->length);
+    if(data.mid(16, 4) == "soun") {
+      break;
+    }
+    trak = 0;
+  }
+  if (!trak) {
+    debug("MP4: No audio tracks");
+    return;
+  }
+
+  MP4::Atom *mdhd = trak->find("mdia", "mdhd");
+  if(!mdhd) {
+    debug("MP4: Atom 'trak.mdia.mdhd' not found");
+    return;
+  }
+
+  file->seek(mdhd->offset);
+  data = file->readBlock(mdhd->length);
+  if(data[8] == 0) {
+    unsigned int unit = data.mid(20, 4).toUInt();
+    unsigned int length = data.mid(24, 4).toUInt();
+    d->length = length / unit;
+  }
+  else {
+    long long unit = data.mid(28, 8).toLongLong();
+    long long length = data.mid(36, 8).toLongLong();
+    d->length = int(length / unit);
+  }
+
+  MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
+  if(!atom) {
+    return;
+  }
+
+  file->seek(atom->offset);
+  data = file->readBlock(atom->length);
+  if(data.mid(20, 4) == "mp4a") {
+    d->channels = data.mid(40, 2).toShort();
+    d->bitsPerSample = data.mid(42, 2).toShort();
+    d->sampleRate = data.mid(46, 4).toUInt();
+    if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
+      long pos = 65;
+      if(data.mid(pos, 3) == "\x80\x80\x80") {
+        pos += 3;
+      }
+      pos += 4;
+      if(data[pos] == 0x04) {
+        pos += 1;
+        if(data.mid(pos, 3) == "\x80\x80\x80") {
+          pos += 3;
+        }
+        pos += 10;
+        d->bitrate = (data.mid(pos, 4).toUInt() + 500) / 1000;
+      }
+    }
+  }
+}
+
+MP4::Properties::~Properties()
+{
+  delete d;
+}
+
+int
+MP4::Properties::channels() const
+{
+  return d->channels;
+}
+
+int
+MP4::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int
+MP4::Properties::length() const
+{
+  return d->length;
+}
+
+int
+MP4::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int
+MP4::Properties::bitsPerSample() const
+{
+  return d->bitsPerSample;
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4properties.h b/src/taglib/mp4/mp4properties.h
new file mode 100644 (file)
index 0000000..fb76c8a
--- /dev/null
@@ -0,0 +1,61 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4PROPERTIES_H
+#define TAGLIB_MP4PROPERTIES_H
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    class Atoms;
+    class File;
+
+    //! An implementation of MP4 audio properties
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      Properties(File *file, Atoms *atoms, ReadStyle style = Average);
+      virtual ~Properties();
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+      virtual int bitsPerSample() const;
+
+    private:
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4tag.cpp b/src/taglib/mp4/mp4tag.cpp
new file mode 100644 (file)
index 0000000..f8acc87
--- /dev/null
@@ -0,0 +1,636 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4atom.h"
+#include "mp4tag.h"
+#include "id3v1genres.h"
+
+using namespace TagLib;
+
+class MP4::Tag::TagPrivate
+{
+public:
+  TagPrivate() : file(0), atoms(0) {}
+  ~TagPrivate() {}
+  TagLib::File *file;
+  Atoms *atoms;
+  ItemListMap items;
+};
+
+MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
+{
+  d = new TagPrivate;
+  d->file = file;
+  d->atoms = atoms;
+
+  MP4::Atom *ilst = atoms->find("moov", "udta", "meta", "ilst");
+  if(!ilst) {
+    //debug("Atom moov.udta.meta.ilst not found.");
+    return;
+  }
+
+  for(unsigned int i = 0; i < ilst->children.size(); i++) {
+    MP4::Atom *atom = ilst->children[i];
+    file->seek(atom->offset + 8);
+    if(atom->name == "----") {
+      parseFreeForm(atom, file);
+    }
+    else if(atom->name == "trkn" || atom->name == "disk") {
+      parseIntPair(atom, file);
+    }
+    else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst") {
+      parseBool(atom, file);
+    }
+    else if(atom->name == "tmpo") {
+      parseInt(atom, file);
+    }
+    else if(atom->name == "gnre") {
+      parseGnre(atom, file);
+    }
+    else if(atom->name == "covr") {
+      parseCovr(atom, file);
+    }
+    else {
+      parseText(atom, file);
+    }
+  }
+}
+
+MP4::Tag::~Tag()
+{
+  delete d;
+}
+
+ByteVectorList
+MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
+{
+  ByteVectorList result;
+  ByteVector data = file->readBlock(atom->length - 8);
+  int i = 0;
+  unsigned int pos = 0;
+  while(pos < data.size()) {
+    int length = data.mid(pos, 4).toUInt();
+    ByteVector name = data.mid(pos + 4, 4);
+    int flags = data.mid(pos + 8, 4).toUInt();
+    if(freeForm && i < 2) {
+      if(i == 0 && name != "mean") {
+        debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
+        return result;
+      }
+      else if(i == 1 && name != "name") {
+        debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
+        return result;
+      }
+      result.append(data.mid(pos + 12, length - 12));
+    }
+    else {
+      if(name != "data") {
+        debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
+        return result;
+      }
+      if(expectedFlags == -1 || flags == expectedFlags) {
+        result.append(data.mid(pos + 16, length - 16));
+      }
+    }
+    pos += length;
+    i++;
+  }
+  return result;
+}
+
+void
+MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file);
+  if(data.size()) {
+    d->items.insert(atom->name, (int)data[0].toShort());
+  }
+}
+
+void
+MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file);
+  if(data.size()) {
+    int idx = (int)data[0].toShort();
+    if(!d->items.contains("\251gen") && idx > 0) {
+      d->items.insert("\251gen", StringList(ID3v1::genre(idx - 1)));
+    }
+  }
+}
+
+void
+MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file);
+  if(data.size()) {
+    int a = data[0].mid(2, 2).toShort();
+    int b = data[0].mid(4, 2).toShort();
+    d->items.insert(atom->name, MP4::Item(a, b));
+  }
+}
+
+void
+MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file);
+  if(data.size()) {
+    bool value = data[0].size() ? data[0][0] != '\0' : false;
+    d->items.insert(atom->name, value);
+  }
+}
+
+void
+MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
+{
+  ByteVectorList data = parseData(atom, file, expectedFlags);
+  if(data.size()) {
+    StringList value;
+    for(unsigned int i = 0; i < data.size(); i++) {
+      value.append(String(data[i], String::UTF8));
+    }
+    d->items.insert(atom->name, value);
+  }
+}
+
+void
+MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file, 1, true);
+  if(data.size() > 2) {
+    StringList value;
+    for(unsigned int i = 2; i < data.size(); i++) {
+      value.append(String(data[i], String::UTF8));
+    }
+    String name = "----:" + data[0] + ':' + data[1];
+    d->items.insert(name, value);
+  }
+}
+
+void
+MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
+{
+  MP4::CoverArtList value;
+  ByteVector data = file->readBlock(atom->length - 8);
+  unsigned int pos = 0;
+  while(pos < data.size()) {
+    int length = data.mid(pos, 4).toUInt();
+    ByteVector name = data.mid(pos + 4, 4);
+    int flags = data.mid(pos + 8, 4).toUInt();
+    if(name != "data") {
+      debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
+      break;
+    }
+    if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) {
+      value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
+                                 data.mid(pos + 16, length - 16)));
+    }
+    pos += length;
+  }
+  if(value.size() > 0)
+    d->items.insert(atom->name, value);
+}
+
+ByteVector
+MP4::Tag::padIlst(const ByteVector &data, int length)
+{
+  if (length == -1) {
+    length = ((data.size() + 1023) & ~1023) - data.size();
+  }
+  return renderAtom("free", ByteVector(length, '\1'));
+}
+
+ByteVector
+MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data)
+{
+  return ByteVector::fromUInt(data.size() + 8) + name + data;
+}
+
+ByteVector
+MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data)
+{
+  ByteVector result;
+  for(unsigned int i = 0; i < data.size(); i++) {
+    result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + data[i]));
+  }
+  return renderAtom(name, result);
+}
+
+ByteVector
+MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
+  return renderData(name, 0x15, data);
+}
+
+ByteVector
+MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector::fromShort(item.toInt()));
+  return renderData(name, 0x15, data);
+}
+
+ByteVector
+MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector(2, '\0') +
+              ByteVector::fromShort(item.toIntPair().first) +
+              ByteVector::fromShort(item.toIntPair().second) +
+              ByteVector(2, '\0'));
+  return renderData(name, 0x00, data);
+}
+
+ByteVector
+MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector(2, '\0') +
+              ByteVector::fromShort(item.toIntPair().first) +
+              ByteVector::fromShort(item.toIntPair().second));
+  return renderData(name, 0x00, data);
+}
+
+ByteVector
+MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags)
+{
+  ByteVectorList data;
+  StringList value = item.toStringList();
+  for(unsigned int i = 0; i < value.size(); i++) {
+    data.append(value[i].data(String::UTF8));
+  }
+  return renderData(name, flags, data);
+}
+
+ByteVector
+MP4::Tag::renderCovr(const ByteVector &name, MP4::Item &item)
+{
+  ByteVector data;
+  MP4::CoverArtList value = item.toCoverArtList();
+  for(unsigned int i = 0; i < value.size(); i++) {
+    data.append(renderAtom("data", ByteVector::fromUInt(value[i].format()) +
+                                   ByteVector(4, '\0') + value[i].data()));
+  }
+  return renderAtom(name, data);
+}
+
+ByteVector
+MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
+{
+  StringList header = StringList::split(name, ":");
+  if (header.size() != 3) {
+    debug("MP4: Invalid free-form item name \"" + name + "\"");
+    return ByteVector::null;
+  }
+  ByteVector data;
+  data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8)));
+  data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8)));
+  StringList value = item.toStringList();
+  for(unsigned int i = 0; i < value.size(); i++) {
+    data.append(renderAtom("data", ByteVector::fromUInt(1) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
+  }
+  return renderAtom("----", data);
+}
+
+bool
+MP4::Tag::save()
+{
+  ByteVector data;
+  for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
+    const String name = i->first;
+    if(name.startsWith("----")) {
+      data.append(renderFreeForm(name, i->second));
+    }
+    else if(name == "trkn") {
+      data.append(renderIntPair(name.data(String::Latin1), i->second));
+    }
+    else if(name == "disk") {
+      data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second));
+    }
+    else if(name == "cpil" || name == "pgap" || name == "pcst") {
+      data.append(renderBool(name.data(String::Latin1), i->second));
+    }
+    else if(name == "tmpo") {
+      data.append(renderInt(name.data(String::Latin1), i->second));
+    }
+    else if(name == "covr") {
+      data.append(renderCovr(name.data(String::Latin1), i->second));
+    }
+    else if(name.size() == 4){
+      data.append(renderText(name.data(String::Latin1), i->second));
+    }
+    else {
+      debug("MP4: Unknown item name \"" + name + "\"");
+    }
+  }
+  data = renderAtom("ilst", data);
+
+  AtomList path = d->atoms->path("moov", "udta", "meta", "ilst");
+  if(path.size() == 4) {
+    saveExisting(data, path);
+  }
+  else {
+    saveNew(data);
+  }
+
+  return true;
+}
+
+void
+MP4::Tag::updateParents(AtomList &path, long delta, int ignore)
+{
+  for(unsigned int i = 0; i < path.size() - ignore; i++) {
+    d->file->seek(path[i]->offset);
+    long size = d->file->readBlock(4).toUInt();
+    // 64-bit
+    if (size == 1) {
+      d->file->seek(4, File::Current); // Skip name
+      long long longSize = d->file->readBlock(8).toLongLong();
+      // Seek the offset of the 64-bit size
+      d->file->seek(path[i]->offset + 8);
+      d->file->writeBlock(ByteVector::fromLongLong(longSize + delta));
+    }
+    // 32-bit
+    else {
+      d->file->seek(path[i]->offset);
+      d->file->writeBlock(ByteVector::fromUInt(size + delta));
+    }
+  }
+}
+
+void
+MP4::Tag::updateOffsets(long delta, long offset)
+{
+  MP4::Atom *moov = d->atoms->find("moov");
+  if(moov) {
+    MP4::AtomList stco = moov->findall("stco", true);
+    for(unsigned int i = 0; i < stco.size(); i++) {
+      MP4::Atom *atom = stco[i];
+      if(atom->offset > offset) {
+        atom->offset += delta;
+      }
+      d->file->seek(atom->offset + 12);
+      ByteVector data = d->file->readBlock(atom->length - 12);
+      unsigned int count = data.mid(0, 4).toUInt();
+      d->file->seek(atom->offset + 16);
+      int pos = 4;
+      while(count--) {
+        long o = data.mid(pos, 4).toUInt();
+        if(o > offset) {
+          o += delta;
+        }
+        d->file->writeBlock(ByteVector::fromUInt(o));
+        pos += 4;
+      }
+    }
+
+    MP4::AtomList co64 = moov->findall("co64", true);
+    for(unsigned int i = 0; i < co64.size(); i++) {
+      MP4::Atom *atom = co64[i];
+      if(atom->offset > offset) {
+        atom->offset += delta;
+      }
+      d->file->seek(atom->offset + 12);
+      ByteVector data = d->file->readBlock(atom->length - 12);
+      unsigned int count = data.mid(0, 4).toUInt();
+      d->file->seek(atom->offset + 16);
+      int pos = 4;
+      while(count--) {
+        long long o = data.mid(pos, 8).toLongLong();
+        if(o > offset) {
+          o += delta;
+        }
+        d->file->writeBlock(ByteVector::fromLongLong(o));
+        pos += 8;
+      }
+    }
+  }
+
+  MP4::Atom *moof = d->atoms->find("moof");
+  if(moof) {
+    MP4::AtomList tfhd = moof->findall("tfhd", true);
+    for(unsigned int i = 0; i < tfhd.size(); i++) {
+      MP4::Atom *atom = tfhd[i];
+      if(atom->offset > offset) {
+        atom->offset += delta;
+      }
+      d->file->seek(atom->offset + 9);
+      ByteVector data = d->file->readBlock(atom->offset - 9);
+      unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt();
+      if(flags & 1) {
+        long long o = data.mid(7, 8).toLongLong();
+        if(o > offset) {
+          o += delta;
+        }
+        d->file->seek(atom->offset + 16);
+        d->file->writeBlock(ByteVector::fromLongLong(o));
+      }
+    }
+  }
+}
+
+void
+MP4::Tag::saveNew(ByteVector &data)
+{
+  data = renderAtom("meta", TagLib::ByteVector(4, '\0') +
+                    renderAtom("hdlr", TagLib::ByteVector(8, '\0') + TagLib::ByteVector("mdirappl") + TagLib::ByteVector(9, '\0')) +
+                    data + padIlst(data));
+
+  AtomList path = d->atoms->path("moov", "udta");
+  if(path.size() != 2) {
+    path = d->atoms->path("moov");
+    data = renderAtom("udta", data);
+  }
+
+  long offset = path[path.size() - 1]->offset + 8;
+  d->file->insert(data, offset, 0);
+
+  updateParents(path, data.size());
+  updateOffsets(data.size(), offset);
+}
+
+void
+MP4::Tag::saveExisting(ByteVector &data, AtomList &path)
+{
+  MP4::Atom *ilst = path[path.size() - 1];
+  long offset = ilst->offset;
+  long length = ilst->length;
+
+  MP4::Atom *meta = path[path.size() - 2];
+  AtomList::Iterator index = meta->children.find(ilst);
+
+  // check if there is an atom before 'ilst', and possibly use it as padding
+  if(index != meta->children.begin()) {
+    AtomList::Iterator prevIndex = index;
+    prevIndex--;
+    MP4::Atom *prev = *prevIndex;
+    if(prev->name == "free") {
+      offset = prev->offset;
+      length += prev->length;
+    }
+  }
+  // check if there is an atom after 'ilst', and possibly use it as padding
+  AtomList::Iterator nextIndex = index;
+  nextIndex++;
+  if(nextIndex != meta->children.end()) {
+    MP4::Atom *next = *nextIndex;
+    if(next->name == "free") {
+      length += next->length;
+    }
+  }
+
+  long delta = data.size() - length;
+  if(delta > 0 || (delta < 0 && delta > -8)) {
+    data.append(padIlst(data));
+    delta = data.size() - length;
+  }
+  else if(delta < 0) {
+    data.append(padIlst(data, -delta - 8));
+    delta = 0;
+  }
+
+  d->file->insert(data, offset, length);
+
+  if(delta) {
+    updateParents(path, delta, 1);
+    updateOffsets(delta, offset);
+  }
+}
+
+String
+MP4::Tag::title() const
+{
+  if(d->items.contains("\251nam"))
+    return d->items["\251nam"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::artist() const
+{
+  if(d->items.contains("\251ART"))
+    return d->items["\251ART"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::album() const
+{
+  if(d->items.contains("\251alb"))
+    return d->items["\251alb"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::comment() const
+{
+  if(d->items.contains("\251cmt"))
+    return d->items["\251cmt"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::genre() const
+{
+  if(d->items.contains("\251gen"))
+    return d->items["\251gen"].toStringList().toString(", ");
+  return String::null;
+}
+
+unsigned int
+MP4::Tag::year() const
+{
+  if(d->items.contains("\251day"))
+    return d->items["\251day"].toStringList().toString().toInt();
+  return 0;
+}
+
+unsigned int
+MP4::Tag::track() const
+{
+  if(d->items.contains("trkn"))
+    return d->items["trkn"].toIntPair().first;
+  return 0;
+}
+
+void
+MP4::Tag::setTitle(const String &value)
+{
+  d->items["\251nam"] = StringList(value);
+}
+
+void
+MP4::Tag::setArtist(const String &value)
+{
+  d->items["\251ART"] = StringList(value);
+}
+
+void
+MP4::Tag::setAlbum(const String &value)
+{
+  d->items["\251alb"] = StringList(value);
+}
+
+void
+MP4::Tag::setComment(const String &value)
+{
+  d->items["\251cmt"] = StringList(value);
+}
+
+void
+MP4::Tag::setGenre(const String &value)
+{
+  d->items["\251gen"] = StringList(value);
+}
+
+void
+MP4::Tag::setYear(uint value)
+{
+  d->items["\251day"] = StringList(String::number(value));
+}
+
+void
+MP4::Tag::setTrack(uint value)
+{
+  d->items["trkn"] = MP4::Item(value, 0);
+}
+
+MP4::ItemListMap &
+MP4::Tag::itemListMap()
+{
+  return d->items;
+}
+
+#endif
diff --git a/src/taglib/mp4/mp4tag.h b/src/taglib/mp4/mp4tag.h
new file mode 100644 (file)
index 0000000..6d7f140
--- /dev/null
@@ -0,0 +1,104 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4TAG_H
+#define TAGLIB_MP4TAG_H
+
+#include "tag.h"
+#include "tbytevectorlist.h"
+#include "tfile.h"
+#include "tmap.h"
+#include "tstringlist.h"
+#include "taglib_export.h"
+#include "mp4atom.h"
+#include "mp4item.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    typedef TagLib::Map<String, Item> ItemListMap;
+
+    class TAGLIB_EXPORT Tag: public TagLib::Tag
+    {
+    public:
+        Tag(TagLib::File *file, Atoms *atoms);
+        ~Tag();
+        bool save();
+
+        String title() const;
+        String artist() const;
+        String album() const;
+        String comment() const;
+        String genre() const;
+        uint year() const;
+        uint track() const;
+
+        void setTitle(const String &value);
+        void setArtist(const String &value);
+        void setAlbum(const String &value);
+        void setComment(const String &value);
+        void setGenre(const String &value);
+        void setYear(uint value);
+        void setTrack(uint value);
+
+        ItemListMap &itemListMap();
+
+    private:
+        TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
+        void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1);
+        void parseFreeForm(Atom *atom, TagLib::File *file);
+        void parseInt(Atom *atom, TagLib::File *file);
+        void parseGnre(Atom *atom, TagLib::File *file);
+        void parseIntPair(Atom *atom, TagLib::File *file);
+        void parseBool(Atom *atom, TagLib::File *file);
+        void parseCovr(Atom *atom, TagLib::File *file);
+
+        TagLib::ByteVector padIlst(const ByteVector &data, int length = -1);
+        TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data);
+        TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data);
+        TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = 1);
+        TagLib::ByteVector renderFreeForm(const String &name, Item &item);
+        TagLib::ByteVector renderBool(const ByteVector &name, Item &item);
+        TagLib::ByteVector renderInt(const ByteVector &name, Item &item);
+        TagLib::ByteVector renderIntPair(const ByteVector &name, Item &item);
+        TagLib::ByteVector renderIntPairNoTrailing(const ByteVector &name, Item &item);
+        TagLib::ByteVector renderCovr(const ByteVector &name, Item &item);
+
+        void updateParents(AtomList &path, long delta, int ignore = 0);
+        void updateOffsets(long delta, long offset);
+
+        void saveNew(TagLib::ByteVector &data);
+        void saveExisting(TagLib::ByteVector &data, AtomList &path);
+
+        class TagPrivate;
+        TagPrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/src/taglib/mpc/mpcfile.cpp b/src/taglib/mpc/mpcfile.cpp
new file mode 100644 (file)
index 0000000..922bf83
--- /dev/null
@@ -0,0 +1,325 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tagunion.h>
+#include <tdebug.h>
+
+#include "mpcfile.h"
+#include "id3v1tag.h"
+#include "id3v2header.h"
+#include "apetag.h"
+#include "apefooter.h"
+
+using namespace TagLib;
+
+namespace
+{
+  enum { APEIndex, ID3v1Index };
+}
+
+class MPC::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    APELocation(-1),
+    APESize(0),
+    ID3v1Location(-1),
+    ID3v2Header(0),
+    ID3v2Location(-1),
+    ID3v2Size(0),
+    properties(0),
+    scanned(false),
+    hasAPE(false),
+    hasID3v1(false),
+    hasID3v2(false) {}
+
+  ~FilePrivate()
+  {
+    delete ID3v2Header;
+    delete properties;
+  }
+
+  long APELocation;
+  uint APESize;
+
+  long ID3v1Location;
+
+  ID3v2::Header *ID3v2Header;
+  long ID3v2Location;
+  uint ID3v2Size;
+
+  TagUnion tag;
+
+  Properties *properties;
+  bool scanned;
+
+  // These indicate whether the file *on disk* has these tags, not if
+  // this data structure does.  This is used in computing offsets.
+
+  bool hasAPE;
+  bool hasID3v1;
+  bool hasID3v2;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPC::File::File(FileName file, bool readProperties,
+                Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+MPC::File::~File()
+{
+  delete d;
+}
+
+TagLib::Tag *MPC::File::tag() const
+{
+  return &d->tag;
+}
+
+MPC::Properties *MPC::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool MPC::File::save()
+{
+  if(readOnly()) {
+    debug("MPC::File::save() -- File is read only.");
+    return false;
+  }
+
+  // Possibly strip ID3v2 tag
+
+  if(d->hasID3v2 && !d->ID3v2Header) {
+    removeBlock(d->ID3v2Location, d->ID3v2Size);
+    d->hasID3v2 = false;
+    if(d->hasID3v1)
+      d->ID3v1Location -= d->ID3v2Size;
+    if(d->hasAPE)
+      d->APELocation -= d->ID3v2Size;
+  }
+
+  // Update ID3v1 tag
+
+  if(ID3v1Tag()) {
+    if(d->hasID3v1) {
+      seek(d->ID3v1Location);
+      writeBlock(ID3v1Tag()->render());
+    }
+    else {
+      seek(0, End);
+      d->ID3v1Location = tell();
+      writeBlock(ID3v1Tag()->render());
+      d->hasID3v1 = true;
+    }
+  } else
+    if(d->hasID3v1) {
+      removeBlock(d->ID3v1Location, 128);
+      d->hasID3v1 = false;
+      if(d->hasAPE) {
+        if(d->APELocation > d->ID3v1Location)
+          d->APELocation -= 128;
+      }
+    }
+
+  // Update APE tag
+
+  if(APETag()) {
+    if(d->hasAPE)
+      insert(APETag()->render(), d->APELocation, d->APESize);
+    else {
+      if(d->hasID3v1)  {
+        insert(APETag()->render(), d->ID3v1Location, 0);
+        d->APESize = APETag()->footer()->completeTagSize();
+        d->hasAPE = true;
+        d->APELocation = d->ID3v1Location;
+        d->ID3v1Location += d->APESize;
+      }
+      else {
+        seek(0, End);
+        d->APELocation = tell();
+        writeBlock(APETag()->render());
+        d->APESize = APETag()->footer()->completeTagSize();
+        d->hasAPE = true;
+      }
+    }
+  }
+  else
+    if(d->hasAPE) {
+      removeBlock(d->APELocation, d->APESize);
+      d->hasAPE = false;
+      if(d->hasID3v1) {
+        if(d->ID3v1Location > d->APELocation)
+          d->ID3v1Location -= d->APESize;
+      }
+    }
+
+  return true;
+}
+
+ID3v1::Tag *MPC::File::ID3v1Tag(bool create)
+{
+  return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
+}
+
+APE::Tag *MPC::File::APETag(bool create)
+{
+  return d->tag.access<APE::Tag>(APEIndex, create);
+}
+
+void MPC::File::strip(int tags)
+{
+  if(tags & ID3v1) {
+    d->tag.set(ID3v1Index, 0);
+    APETag(true);
+  }
+
+  if(tags & ID3v2) {
+    delete d->ID3v2Header;
+    d->ID3v2Header = 0;
+  }
+
+  if(tags & APE) {
+    d->tag.set(APEIndex, 0);
+
+    if(!ID3v1Tag())
+      APETag(true);
+  }
+}
+
+void MPC::File::remove(int tags)
+{
+  strip(tags);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
+{
+  // Look for an ID3v1 tag
+
+  d->ID3v1Location = findID3v1();
+
+  if(d->ID3v1Location >= 0) {
+    d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
+    d->hasID3v1 = true;
+  }
+
+  // Look for an APE tag
+
+  findAPE();
+
+  d->APELocation = findAPE();
+
+  if(d->APELocation >= 0) {
+    d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
+
+    d->APESize = APETag()->footer()->completeTagSize();
+    d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
+    d->hasAPE = true;
+  }
+
+  if(!d->hasID3v1)
+    APETag(true);
+
+  // Look for and skip an ID3v2 tag
+
+  d->ID3v2Location = findID3v2();
+
+  if(d->ID3v2Location >= 0) {
+    seek(d->ID3v2Location);
+    d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
+    d->ID3v2Size = d->ID3v2Header->completeTagSize();
+    d->hasID3v2 = true;
+  }
+
+  if(d->hasID3v2)
+    seek(d->ID3v2Location + d->ID3v2Size);
+  else
+    seek(0);
+
+  // Look for MPC metadata
+
+  if(readProperties) {
+    d->properties = new Properties(readBlock(MPC::HeaderSize),
+                                   length() - d->ID3v2Size - d->APESize);
+  }
+}
+
+long MPC::File::findAPE()
+{
+  if(!isValid())
+    return -1;
+
+  if(d->hasID3v1)
+    seek(-160, End);
+  else
+    seek(-32, End);
+
+  long p = tell();
+
+  if(readBlock(8) == APE::Tag::fileIdentifier())
+    return p;
+
+  return -1;
+}
+
+long MPC::File::findID3v1()
+{
+  if(!isValid())
+    return -1;
+
+  seek(-128, End);
+  long p = tell();
+
+  if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+    return p;
+
+  return -1;
+}
+
+long MPC::File::findID3v2()
+{
+  if(!isValid())
+    return -1;
+
+  seek(0);
+
+  if(readBlock(3) == ID3v2::Header::fileIdentifier())
+    return 0;
+
+  return -1;
+}
diff --git a/src/taglib/mpc/mpcfile.h b/src/taglib/mpc/mpcfile.h
new file mode 100644 (file)
index 0000000..7e34c86
--- /dev/null
@@ -0,0 +1,175 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MPCFILE_H
+#define TAGLIB_MPCFILE_H
+
+#include "taglib_export.h"
+#include "tfile.h"
+
+#include "mpcproperties.h"
+
+namespace TagLib {
+
+  class Tag;
+
+  namespace ID3v1 { class Tag; }
+  namespace APE { class Tag; }
+
+  //! An implementation of MPC metadata
+
+  /*!
+   * This is implementation of MPC metadata.
+   *
+   * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
+   * properties from the file. ID3v2 tags are invalid in MPC-files, but will be skipped
+   * and ignored.
+   */
+
+  namespace MPC {
+
+    //! An implementation of TagLib::File with MPC specific methods
+
+    /*!
+     * This implements and provides an interface for MPC files to the
+     * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+     * the abstract TagLib::File API as well as providing some additional
+     * information specific to MPC files.
+     * The only invalid tag combination supported is an ID3v1 tag after an APE tag.
+     */
+
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * This set of flags is used for various operations and is suitable for
+       * being OR-ed together.
+       */
+      enum TagTypes {
+        //! Empty set.  Matches no tag types.
+        NoTags  = 0x0000,
+        //! Matches ID3v1 tags.
+        ID3v1   = 0x0001,
+        //! Matches ID3v2 tags.
+        ID3v2   = 0x0002,
+        //! Matches APE tags.
+        APE     = 0x0004,
+        //! Matches all tag types.
+        AllTags = 0xffff
+      };
+
+      /*!
+       * Contructs an MPC file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       */
+      File(FileName file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns the Tag for this file.  This will be an APE tag, an ID3v1 tag
+       * or a combination of the two.
+       */
+      virtual TagLib::Tag *tag() const;
+
+      /*!
+       * Returns the MPC::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Saves the file.
+       */
+      virtual bool save();
+
+      /*!
+       * Returns a pointer to the ID3v1 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v1 tag.  If \a create is true it will create
+       * an ID3v1 tag if one does not exist. If there is already an APE tag, the
+       * new ID3v1 tag will be placed after it.
+       *
+       * \note The Tag <b>is still</b> owned by the APE::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v1::Tag *ID3v1Tag(bool create = false);
+
+      /*!
+       * Returns a pointer to the APE tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid APE tag.  If \a create is true it will create
+       * a APE tag if one does not exist. If there is already an ID3v1 tag, thes
+       * new APE tag will be placed before it.
+       *
+       * \note The Tag <b>is still</b> owned by the APE::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      APE::Tag *APETag(bool create = false);
+
+      /*!
+       * This will remove the tags that match the OR-ed together TagTypes from the
+       * file.  By default it removes all tags.
+       *
+       * \warning This will also invalidate pointers to the tags
+       * as their memory will be freed.
+       *
+       * \note In order to make the removal permanent save() still needs to be called.
+       */
+      void strip(int tags = AllTags);
+
+      /*!
+       * \deprecated
+       * \see strip
+       */
+      void remove(int tags = AllTags);
+
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+      void scan();
+      long findAPE();
+      long findID3v1();
+      long findID3v2();
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpc/mpcproperties.cpp b/src/taglib/mpc/mpcproperties.cpp
new file mode 100644 (file)
index 0000000..2114a86
--- /dev/null
@@ -0,0 +1,140 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <bitset>
+
+#include "mpcproperties.h"
+#include "mpcfile.h"
+
+using namespace TagLib;
+
+class MPC::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
+    data(d),
+    streamLength(length),
+    style(s),
+    version(0),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0) {}
+
+  ByteVector data;
+  long streamLength;
+  ReadStyle style;
+  int version;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(data, streamLength, style);
+  read();
+}
+
+MPC::Properties::~Properties()
+{
+  delete d;
+}
+
+int MPC::Properties::length() const
+{
+  return d->length;
+}
+
+int MPC::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int MPC::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int MPC::Properties::channels() const
+{
+  return d->channels;
+}
+
+int MPC::Properties::mpcVersion() const
+{
+  return d->version;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 };
+
+void MPC::Properties::read()
+{
+  if(!d->data.startsWith("MP+"))
+    return;
+
+  d->version = d->data[3] & 15;
+
+  unsigned int frames;
+
+  if(d->version >= 7) {
+    frames = d->data.mid(4, 4).toUInt(false);
+
+    std::bitset<32> flags = d->data.mid(8, 4).toUInt(false);
+    d->sampleRate = sftable[flags[17] * 2 + flags[16]];
+    d->channels = 2;
+  }
+  else {
+    uint headerData = d->data.mid(0, 4).toUInt(false);
+
+    d->bitrate = (headerData >> 23) & 0x01ff;
+    d->version = (headerData >> 11) & 0x03ff;
+    d->sampleRate = 44100;
+    d->channels = 2;
+
+    if(d->version >= 5)
+      frames = d->data.mid(4, 4).toUInt(false);
+    else
+      frames = d->data.mid(6, 2).toUInt(false);
+  }
+
+  uint samples = frames * 1152 - 576;
+
+  d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
+
+  if(!d->bitrate)
+    d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
+}
diff --git a/src/taglib/mpc/mpcproperties.h b/src/taglib/mpc/mpcproperties.h
new file mode 100644 (file)
index 0000000..bdbc887
--- /dev/null
@@ -0,0 +1,85 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MPCPROPERTIES_H
+#define TAGLIB_MPCPROPERTIES_H
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace MPC {
+
+    class File;
+
+    static const uint HeaderSize = 8*7;
+
+    //! An implementation of audio property reading for MPC
+
+    /*!
+     * This reads the data from an MPC stream found in the AudioProperties
+     * API.
+     */
+
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      /*!
+       * Create an instance of MPC::Properties with the data read from the
+       * ByteVector \a data.
+       */
+      Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
+
+      /*!
+       * Destroys this MPC::Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+      /*!
+       * Returns the version of the bitstream (SV4-SV7)
+       */
+      int mpcVersion() const;
+
+    private:
+      Properties(const Properties &);
+      Properties &operator=(const Properties &);
+
+      void read();
+
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v1/id3v1genres.cpp b/src/taglib/mpeg/id3v1/id3v1genres.cpp
new file mode 100644 (file)
index 0000000..7cd42f3
--- /dev/null
@@ -0,0 +1,219 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "id3v1genres.h"
+
+using namespace TagLib;
+
+namespace TagLib {
+  namespace ID3v1 {
+
+    static const int genresSize = 148;
+    static const String genres[] = {
+      "Blues",
+      "Classic Rock",
+      "Country",
+      "Dance",
+      "Disco",
+      "Funk",
+      "Grunge",
+      "Hip-Hop",
+      "Jazz",
+      "Metal",
+      "New Age",
+      "Oldies",
+      "Other",
+      "Pop",
+      "R&B",
+      "Rap",
+      "Reggae",
+      "Rock",
+      "Techno",
+      "Industrial",
+      "Alternative",
+      "Ska",
+      "Death Metal",
+      "Pranks",
+      "Soundtrack",
+      "Euro-Techno",
+      "Ambient",
+      "Trip-Hop",
+      "Vocal",
+      "Jazz+Funk",
+      "Fusion",
+      "Trance",
+      "Classical",
+      "Instrumental",
+      "Acid",
+      "House",
+      "Game",
+      "Sound Clip",
+      "Gospel",
+      "Noise",
+      "Alternative Rock",
+      "Bass",
+      "Soul",
+      "Punk",
+      "Space",
+      "Meditative",
+      "Instrumental Pop",
+      "Instrumental Rock",
+      "Ethnic",
+      "Gothic",
+      "Darkwave",
+      "Techno-Industrial",
+      "Electronic",
+      "Pop-Folk",
+      "Eurodance",
+      "Dream",
+      "Southern Rock",
+      "Comedy",
+      "Cult",
+      "Gangsta",
+      "Top 40",
+      "Christian Rap",
+      "Pop/Funk",
+      "Jungle",
+      "Native American",
+      "Cabaret",
+      "New Wave",
+      "Psychedelic",
+      "Rave",
+      "Showtunes",
+      "Trailer",
+      "Lo-Fi",
+      "Tribal",
+      "Acid Punk",
+      "Acid Jazz",
+      "Polka",
+      "Retro",
+      "Musical",
+      "Rock & Roll",
+      "Hard Rock",
+      "Folk",
+      "Folk/Rock",
+      "National Folk",
+      "Swing",
+      "Fusion",
+      "Bebob",
+      "Latin",
+      "Revival",
+      "Celtic",
+      "Bluegrass",
+      "Avantgarde",
+      "Gothic Rock",
+      "Progressive Rock",
+      "Psychedelic Rock",
+      "Symphonic Rock",
+      "Slow Rock",
+      "Big Band",
+      "Chorus",
+      "Easy Listening",
+      "Acoustic",
+      "Humour",
+      "Speech",
+      "Chanson",
+      "Opera",
+      "Chamber Music",
+      "Sonata",
+      "Symphony",
+      "Booty Bass",
+      "Primus",
+      "Porn Groove",
+      "Satire",
+      "Slow Jam",
+      "Club",
+      "Tango",
+      "Samba",
+      "Folklore",
+      "Ballad",
+      "Power Ballad",
+      "Rhythmic Soul",
+      "Freestyle",
+      "Duet",
+      "Punk Rock",
+      "Drum Solo",
+      "A Cappella",
+      "Euro-House",
+      "Dance Hall",
+      "Goa",
+      "Drum & Bass",
+      "Club-House",
+      "Hardcore",
+      "Terror",
+      "Indie",
+      "BritPop",
+      "Negerpunk",
+      "Polsk Punk",
+      "Beat",
+      "Christian Gangsta Rap",
+      "Heavy Metal",
+      "Black Metal",
+      "Crossover",
+      "Contemporary Christian",
+      "Christian Rock",
+      "Merengue",
+      "Salsa",
+      "Thrash Metal",
+      "Anime",
+      "Jpop",
+      "Synthpop"
+    };
+  }
+}
+
+StringList ID3v1::genreList()
+{
+  static StringList l;
+  if(l.isEmpty()) {
+    for(int i = 0; i < genresSize; i++)
+      l.append(genres[i]);
+  }
+  return l;
+}
+
+ID3v1::GenreMap ID3v1::genreMap()
+{
+  static GenreMap m;
+  if(m.isEmpty()) {
+    for(int i = 0; i < genresSize; i++)
+      m.insert(genres[i], i);
+  }
+  return m;
+}
+
+String ID3v1::genre(int i)
+{
+  if(i >= 0 && i < genresSize)
+    return genres[i];
+  return String::null;
+}
+
+int ID3v1::genreIndex(const String &name)
+{
+  if(genreMap().contains(name))
+    return genreMap()[name];
+  return 255;
+}
diff --git a/src/taglib/mpeg/id3v1/id3v1genres.h b/src/taglib/mpeg/id3v1/id3v1genres.h
new file mode 100644 (file)
index 0000000..cb79056
--- /dev/null
@@ -0,0 +1,66 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V1GENRE_H
+#define TAGLIB_ID3V1GENRE_H
+
+#include "tmap.h"
+#include "tstringlist.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+  namespace ID3v1 {
+
+    typedef Map<String, int> GenreMap;
+
+    /*!
+     * Returns the list of canonical ID3v1 genre names in the order that they
+     * are listed in the standard.
+     */
+    StringList TAGLIB_EXPORT genreList();
+
+    /*!
+     * A "reverse mapping" that goes from the canonical ID3v1 genre name to the
+     * respective genre number.   genreMap()["Rock"] ==
+     */
+    GenreMap TAGLIB_EXPORT genreMap();
+
+    /*!
+     * Returns the name of the genre at \a index in the ID3v1 genre list.  If
+     * \a index is out of range -- less than zero or greater than 146 -- a null
+     * string will be returned.
+     */
+    String TAGLIB_EXPORT genre(int index);
+
+    /*!
+     * Returns the genre index for the (case sensitive) genre \a name.  If the
+     * genre is not in the list 255 (which signifies an unknown genre in ID3v1)
+     * will be returned.
+     */
+    int TAGLIB_EXPORT genreIndex(const String &name);
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v1/id3v1tag.cpp b/src/taglib/mpeg/id3v1/id3v1tag.cpp
new file mode 100644 (file)
index 0000000..490f8dd
--- /dev/null
@@ -0,0 +1,248 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tdebug.h>
+#include <tfile.h>
+
+#include "id3v1tag.h"
+#include "id3v1genres.h"
+
+using namespace TagLib;
+using namespace ID3v1;
+
+class ID3v1::Tag::TagPrivate
+{
+public:
+  TagPrivate() : file(0), tagOffset(-1), track(0), genre(255) {}
+
+  File *file;
+  long tagOffset;
+
+  String title;
+  String artist;
+  String album;
+  String year;
+  String comment;
+  uchar track;
+  uchar genre;
+
+  static const StringHandler *stringHandler;
+};
+
+const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = new StringHandler;
+
+////////////////////////////////////////////////////////////////////////////////
+// StringHandler implementation
+////////////////////////////////////////////////////////////////////////////////
+
+String ID3v1::StringHandler::parse(const ByteVector &data) const
+{
+  return String(data, String::Latin1).stripWhiteSpace();
+}
+
+ByteVector ID3v1::StringHandler::render(const String &s) const
+{
+  if(!s.isLatin1())
+  {
+    return ByteVector();
+  }
+
+  return s.data(String::Latin1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+ID3v1::Tag::Tag() : TagLib::Tag()
+{
+  d = new TagPrivate;
+}
+
+ID3v1::Tag::Tag(File *file, long tagOffset) : TagLib::Tag()
+{
+  d = new TagPrivate;
+  d->file = file;
+  d->tagOffset = tagOffset;
+
+  read();
+}
+
+ID3v1::Tag::~Tag()
+{
+  delete d;
+}
+
+ByteVector ID3v1::Tag::render() const
+{
+  ByteVector data;
+
+  data.append(fileIdentifier());
+  data.append(TagPrivate::stringHandler->render(d->title).resize(30));
+  data.append(TagPrivate::stringHandler->render(d->artist).resize(30));
+  data.append(TagPrivate::stringHandler->render(d->album).resize(30));
+  data.append(TagPrivate::stringHandler->render(d->year).resize(4));
+  data.append(TagPrivate::stringHandler->render(d->comment).resize(28));
+  data.append(char(0));
+  data.append(char(d->track));
+  data.append(char(d->genre));
+
+  return data;
+}
+
+ByteVector ID3v1::Tag::fileIdentifier()
+{
+  return ByteVector::fromCString("TAG");
+}
+
+String ID3v1::Tag::title() const
+{
+  return d->title;
+}
+
+String ID3v1::Tag::artist() const
+{
+  return d->artist;
+}
+
+String ID3v1::Tag::album() const
+{
+  return d->album;
+}
+
+String ID3v1::Tag::comment() const
+{
+  return d->comment;
+}
+
+String ID3v1::Tag::genre() const
+{
+  return ID3v1::genre(d->genre);
+}
+
+TagLib::uint ID3v1::Tag::year() const
+{
+  return d->year.toInt();
+}
+
+TagLib::uint ID3v1::Tag::track() const
+{
+  return d->track;
+}
+
+void ID3v1::Tag::setTitle(const String &s)
+{
+  d->title = s;
+}
+
+void ID3v1::Tag::setArtist(const String &s)
+{
+  d->artist = s;
+}
+
+void ID3v1::Tag::setAlbum(const String &s)
+{
+  d->album = s;
+}
+
+void ID3v1::Tag::setComment(const String &s)
+{
+  d->comment = s;
+}
+
+void ID3v1::Tag::setGenre(const String &s)
+{
+  d->genre = ID3v1::genreIndex(s);
+}
+
+void ID3v1::Tag::setYear(uint i)
+{
+  d->year = i > 0 ? String::number(i) : String::null;
+}
+
+void ID3v1::Tag::setTrack(uint i)
+{
+  d->track = i < 256 ? i : 0;
+}
+
+void ID3v1::Tag::setStringHandler(const StringHandler *handler)
+{
+  delete TagPrivate::stringHandler;
+  TagPrivate::stringHandler = handler;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+void ID3v1::Tag::read()
+{
+  if(d->file && d->file->isValid()) {
+    d->file->seek(d->tagOffset);
+    // read the tag -- always 128 bytes
+    ByteVector data = d->file->readBlock(128);
+
+    // some initial sanity checking
+    if(data.size() == 128 && data.startsWith("TAG"))
+      parse(data);
+    else
+      debug("ID3v1 tag is not valid or could not be read at the specified offset.");
+  }
+}
+
+void ID3v1::Tag::parse(const ByteVector &data)
+{
+  int offset = 3;
+
+  d->title = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+  offset += 30;
+
+  d->artist = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+  offset += 30;
+
+  d->album = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+  offset += 30;
+
+  d->year = TagPrivate::stringHandler->parse(data.mid(offset, 4));
+  offset += 4;
+
+  // Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this
+  // is not a bug in TagLib.  Since a zeroed byte is what we would expect to
+  // indicate the end of a C-String, specifically the comment string, a value of
+  // zero must be assumed to be just that.
+
+  if(data[offset + 28] == 0 && data[offset + 29] != 0) {
+    // ID3v1.1 detected
+
+    d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 28));
+    d->track = uchar(data[offset + 29]);
+  }
+  else
+    d->comment = data.mid(offset, 30);
+
+  offset += 30;
+
+  d->genre = uchar(data[offset]);
+}
diff --git a/src/taglib/mpeg/id3v1/id3v1tag.h b/src/taglib/mpeg/id3v1/id3v1tag.h
new file mode 100644 (file)
index 0000000..629d589
--- /dev/null
@@ -0,0 +1,181 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V1TAG_H
+#define TAGLIB_ID3V1TAG_H
+
+#include "tag.h"
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  class File;
+
+  //! An ID3v1 implementation
+
+  namespace ID3v1 {
+
+    //! A abstraction for the string to data encoding in ID3v1 tags.
+
+    /*!
+     * ID3v1 should in theory always contain ISO-8859-1 (Latin1) data.  In
+     * practice it does not.  TagLib by default only supports ISO-8859-1 data
+     * in ID3v1 tags.
+     *
+     * However by subclassing this class and reimplementing parse() and render()
+     * and setting your reimplementation as the default with
+     * ID3v1::Tag::setStringHandler() you can define how you would like these
+     * transformations to be done.
+     *
+     * \warning It is advisable <b>not</b> to write non-ISO-8859-1 data to ID3v1
+     * tags.  Please consider disabling the writing of ID3v1 tags in the case
+     * that the data is not ISO-8859-1.
+     *
+     * \see ID3v1::Tag::setStringHandler()
+     */
+
+    class TAGLIB_EXPORT StringHandler
+    {
+      TAGLIB_IGNORE_MISSING_DESTRUCTOR
+    public:
+      // BIC: Add virtual destructor.
+
+      /*!
+       * Decode a string from \a data.  The default implementation assumes that
+       * \a data is an ISO-8859-1 (Latin1) character array.
+       */
+      virtual String parse(const ByteVector &data) const;
+
+      /*!
+       * Encode a ByteVector with the data from \a s.  The default implementation
+       * assumes that \a s is an ISO-8859-1 (Latin1) string.  If the string is
+       * does not conform to ISO-8859-1, no value is written.
+       *
+       * \warning It is recommended that you <b>not</b> override this method, but
+       * instead do not write an ID3v1 tag in the case that the data is not
+       * ISO-8859-1.
+       */
+      virtual ByteVector render(const String &s) const;
+    };
+
+    //! The main class in the ID3v1 implementation
+
+    /*!
+     * This is an implementation of the ID3v1 format.  ID3v1 is both the simplist
+     * and most common of tag formats but is rather limited.  Because of its
+     * pervasiveness and the way that applications have been written around the
+     * fields that it provides, the generic TagLib::Tag API is a mirror of what is
+     * provided by ID3v1.
+     *
+     * ID3v1 tags should generally only contain Latin1 information.  However because
+     * many applications do not follow this rule there is now support for overriding
+     * the ID3v1 string handling using the ID3v1::StringHandler class.  Please see
+     * the documentation for that class for more information.
+     *
+     * \see StringHandler
+     *
+     * \note Most fields are truncated to a maximum of 28-30 bytes.  The
+     * truncation happens automatically when the tag is rendered.
+     */
+
+    class TAGLIB_EXPORT Tag : public TagLib::Tag
+    {
+    public:
+      /*!
+       * Create an ID3v1 tag with default values.
+       */
+      Tag();
+
+      /*!
+       * Create an ID3v1 tag and parse the data in \a file starting at
+       * \a tagOffset.
+       */
+      Tag(File *file, long tagOffset);
+
+      /*!
+       * Destroys this Tag instance.
+       */
+      virtual ~Tag();
+
+      /*!
+       * Renders the in memory values to a ByteVector suitable for writing to
+       * the file.
+       */
+      ByteVector render() const;
+
+      /*!
+       * Returns the string "TAG" suitable for usage in locating the tag in a
+       * file.
+       */
+      static ByteVector fileIdentifier();
+
+      // Reimplementations.
+
+      virtual String title() const;
+      virtual String artist() const;
+      virtual String album() const;
+      virtual String comment() const;
+      virtual String genre() const;
+      virtual uint year() const;
+      virtual uint track() const;
+
+      virtual void setTitle(const String &s);
+      virtual void setArtist(const String &s);
+      virtual void setAlbum(const String &s);
+      virtual void setComment(const String &s);
+      virtual void setGenre(const String &s);
+      virtual void setYear(uint i);
+      virtual void setTrack(uint i);
+
+      /*!
+       * Sets the string handler that decides how the ID3v1 data will be
+       * converted to and from binary data.
+       *
+       * \see StringHandler
+       */
+      static void setStringHandler(const StringHandler *handler);
+
+    protected:
+      /*!
+       * Reads from the file specified in the constructor.
+       */
+      void read();
+      /*!
+       * Pareses the body of the tag in \a data.
+       */
+      void parse(const ByteVector &data);
+
+    private:
+      Tag(const Tag &);
+      Tag &operator=(const Tag &);
+
+      class TagPrivate;
+      TagPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp b/src/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp
new file mode 100644 (file)
index 0000000..a763e18
--- /dev/null
@@ -0,0 +1,224 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "attachedpictureframe.h"
+
+#include <tstringlist.h>
+#include <tdebug.h>
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class AttachedPictureFrame::AttachedPictureFramePrivate
+{
+public:
+  AttachedPictureFramePrivate() : textEncoding(String::Latin1),
+                                  type(AttachedPictureFrame::Other) {}
+
+  String::Type textEncoding;
+  String mimeType;
+  AttachedPictureFrame::Type type;
+  String description;
+  ByteVector data;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC")
+{
+  d = new AttachedPictureFramePrivate;
+}
+
+AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data)
+{
+  d = new AttachedPictureFramePrivate;
+  setData(data);
+}
+
+AttachedPictureFrame::~AttachedPictureFrame()
+{
+  delete d;
+}
+
+String AttachedPictureFrame::toString() const
+{
+  String s = "[" + d->mimeType + "]";
+  return d->description.isEmpty() ? s : d->description + " " + s;
+}
+
+String::Type AttachedPictureFrame::textEncoding() const
+{
+  return d->textEncoding;
+}
+
+void AttachedPictureFrame::setTextEncoding(String::Type t)
+{
+  d->textEncoding = t;
+}
+
+String AttachedPictureFrame::mimeType() const
+{
+  return d->mimeType;
+}
+
+void AttachedPictureFrame::setMimeType(const String &m)
+{
+  d->mimeType = m;
+}
+
+AttachedPictureFrame::Type AttachedPictureFrame::type() const
+{
+  return d->type;
+}
+
+void AttachedPictureFrame::setType(Type t)
+{
+  d->type = t;
+}
+
+String AttachedPictureFrame::description() const
+{
+  return d->description;
+}
+
+void AttachedPictureFrame::setDescription(const String &desc)
+{
+  d->description = desc;
+}
+
+ByteVector AttachedPictureFrame::picture() const
+{
+  return d->data;
+}
+
+void AttachedPictureFrame::setPicture(const ByteVector &p)
+{
+  d->data = p;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void AttachedPictureFrame::parseFields(const ByteVector &data)
+{
+  if(data.size() < 5) {
+    debug("A picture frame must contain at least 5 bytes.");
+    return;
+  }
+
+  d->textEncoding = String::Type(data[0]);
+
+  int pos = 1;
+
+  d->mimeType = readStringField(data, String::Latin1, &pos);
+  /* Now we need at least two more bytes available */  
+  if (uint(pos) + 1 >= data.size()) {
+    debug("Truncated picture frame.");
+    return;
+  }
+
+  d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
+  d->description = readStringField(data, d->textEncoding, &pos);
+
+  d->data = data.mid(pos);
+}
+
+ByteVector AttachedPictureFrame::renderFields() const
+{
+  ByteVector data;
+
+  String::Type encoding = checkEncoding(d->description, d->textEncoding);
+
+  data.append(char(encoding));
+  data.append(d->mimeType.data(String::Latin1));
+  data.append(textDelimiter(String::Latin1));
+  data.append(char(d->type));
+  data.append(d->description.data(encoding));
+  data.append(textDelimiter(encoding));
+  data.append(d->data);
+
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new AttachedPictureFramePrivate;
+  parseFields(fieldData(data));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// support for ID3v2.2 PIC frames
+////////////////////////////////////////////////////////////////////////////////
+
+void AttachedPictureFrameV22::parseFields(const ByteVector &data)
+{
+  if(data.size() < 5) {
+    debug("A picture frame must contain at least 5 bytes.");
+    return;
+  }
+
+  d->textEncoding = String::Type(data[0]);
+
+  int pos = 1;
+
+  String fixedString = String(data.mid(pos, 3), String::Latin1);
+  pos += 3;
+  // convert fixed string image type to mime string
+  if (fixedString.upper() == "JPG") {
+    d->mimeType = "image/jpeg";
+  } else if (fixedString.upper() == "PNG") {
+    d->mimeType = "image/png";
+  } else {
+    debug("probably unsupported image type");
+    d->mimeType = "image/" + fixedString;
+  }
+
+  d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
+  d->description = readStringField(data, d->textEncoding, &pos);
+
+  d->data = data.mid(pos);
+}
+
+AttachedPictureFrameV22::AttachedPictureFrameV22(const ByteVector &data, Header *h)
+{
+  d = new AttachedPictureFramePrivate;
+
+  // set v2.2 header to make fieldData work correctly
+  setHeader(h, true);
+
+  parseFields(fieldData(data));
+
+  // now set the v2.4 header
+  Frame::Header *newHeader = new Frame::Header("APIC");
+  newHeader->setFrameSize(h->frameSize());
+  setHeader(newHeader, false);
+}
diff --git a/src/taglib/mpeg/id3v2/frames/attachedpictureframe.h b/src/taglib/mpeg/id3v2/frames/attachedpictureframe.h
new file mode 100644 (file)
index 0000000..bd3453c
--- /dev/null
@@ -0,0 +1,230 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ATTACHEDPICTUREFRAME_H
+#define TAGLIB_ATTACHEDPICTUREFRAME_H
+
+#include "id3v2frame.h"
+#include "id3v2header.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! An ID3v2 attached picture frame implementation
+
+    /*!
+     * This is an implementation of ID3v2 attached pictures.  Pictures may be
+     * included in tags, one per APIC frame (but there may be multiple APIC
+     * frames in a single tag).  These pictures are usually in either JPEG or
+     * PNG format.
+     */
+
+    class TAGLIB_EXPORT AttachedPictureFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+
+      /*!
+       * This describes the function or content of the picture.
+       */
+      enum Type {
+        //! A type not enumerated below
+        Other              = 0x00,
+        //! 32x32 PNG image that should be used as the file icon
+        FileIcon           = 0x01,
+        //! File icon of a different size or format
+        OtherFileIcon      = 0x02,
+        //! Front cover image of the album
+        FrontCover         = 0x03,
+        //! Back cover image of the album
+        BackCover          = 0x04,
+        //! Inside leaflet page of the album
+        LeafletPage        = 0x05,
+        //! Image from the album itself
+        Media              = 0x06,
+        //! Picture of the lead artist or soloist
+        LeadArtist         = 0x07,
+        //! Picture of the artist or performer
+        Artist             = 0x08,
+        //! Picture of the conductor
+        Conductor          = 0x09,
+        //! Picture of the band or orchestra
+        Band               = 0x0A,
+        //! Picture of the composer
+        Composer           = 0x0B,
+        //! Picture of the lyricist or text writer
+        Lyricist           = 0x0C,
+        //! Picture of the recording location or studio
+        RecordingLocation  = 0x0D,
+        //! Picture of the artists during recording
+        DuringRecording    = 0x0E,
+        //! Picture of the artists during performance
+        DuringPerformance  = 0x0F,
+        //! Picture from a movie or video related to the track
+        MovieScreenCapture = 0x10,
+        //! Picture of a large, coloured fish
+        ColouredFish       = 0x11,
+        //! Illustration related to the track
+        Illustration       = 0x12,
+        //! Logo of the band or performer
+        BandLogo           = 0x13,
+        //! Logo of the publisher (record company)
+        PublisherLogo      = 0x14
+      };
+
+      /*!
+       * Constructs an empty picture frame.  The description, content and text
+       * encoding should be set manually.
+       */
+      AttachedPictureFrame();
+
+      /*!
+       * Constructs an AttachedPicture frame based on \a data.
+       */
+      explicit AttachedPictureFrame(const ByteVector &data);
+
+      /*!
+       * Destroys the AttahcedPictureFrame instance.
+       */
+      virtual ~AttachedPictureFrame();
+
+      /*!
+       * Returns a string containing the description and mime-type
+       */
+      virtual String toString() const;
+
+      /*!
+       * Returns the text encoding used for the description.
+       *
+       * \see setTextEncoding()
+       * \see description()
+       */
+      String::Type textEncoding() const;
+
+      /*!
+       * Set the text encoding used for the description.
+       *
+       * \see description()
+       */
+      void setTextEncoding(String::Type t);
+
+      /*!
+       * Returns the mime type of the image.  This should in most cases be
+       * "image/png" or "image/jpeg".
+       */
+      String mimeType() const;
+
+      /*!
+       * Sets the mime type of the image.  This should in most cases be
+       * "image/png" or "image/jpeg".
+       */
+      void setMimeType(const String &m);
+
+      /*!
+       * Returns the type of the image.
+       *
+       * \see Type
+       * \see setType()
+       */
+      Type type() const;
+
+      /*!
+       * Sets the type for the image.
+       *
+       * \see Type
+       * \see type()
+       */
+      void setType(Type t);
+
+      /*!
+       * Returns a text description of the image.
+       *
+       * \see setDescription()
+       * \see textEncoding()
+       * \see setTextEncoding()
+       */
+
+      String description() const;
+
+      /*!
+       * Sets a textual description of the image to \a desc.
+       *
+       * \see description()
+       * \see textEncoding()
+       * \see setTextEncoding()
+       */
+
+      void setDescription(const String &desc);
+
+      /*!
+       * Returns the image data as a ByteVector.
+       *
+       * \note ByteVector has a data() method that returns a const char * which
+       * should make it easy to export this data to external programs.
+       *
+       * \see setPicture()
+       * \see mimeType()
+       */
+      ByteVector picture() const;
+
+      /*!
+       * Sets the image data to \a p.  \a p should be of the type specified in
+       * this frame's mime-type specification.
+       *
+       * \see picture()
+       * \see mimeType()
+       * \see setMimeType()
+       */
+      void setPicture(const ByteVector &p);
+
+    protected:
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+      class AttachedPictureFramePrivate;
+      AttachedPictureFramePrivate *d;
+
+    private:
+      AttachedPictureFrame(const AttachedPictureFrame &);
+      AttachedPictureFrame &operator=(const AttachedPictureFrame &);
+      AttachedPictureFrame(const ByteVector &data, Header *h);
+
+    };
+
+    //! support for ID3v2.2 PIC frames
+    class TAGLIB_EXPORT AttachedPictureFrameV22 : public AttachedPictureFrame
+    {
+    protected:
+      virtual void parseFields(const ByteVector &data);
+    private:
+      AttachedPictureFrameV22(const ByteVector &data, Header *h);
+      friend class FrameFactory;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/commentsframe.cpp b/src/taglib/mpeg/id3v2/frames/commentsframe.cpp
new file mode 100644 (file)
index 0000000..2df176b
--- /dev/null
@@ -0,0 +1,178 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevectorlist.h>
+#include <id3v2tag.h>
+#include <tdebug.h>
+#include <tstringlist.h>
+
+#include "commentsframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class CommentsFrame::CommentsFramePrivate
+{
+public:
+  CommentsFramePrivate() : textEncoding(String::Latin1) {}
+  String::Type textEncoding;
+  ByteVector language;
+  String description;
+  String text;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM")
+{
+  d = new CommentsFramePrivate;
+  d->textEncoding = encoding;
+}
+
+CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data)
+{
+  d = new CommentsFramePrivate;
+  setData(data);
+}
+
+CommentsFrame::~CommentsFrame()
+{
+  delete d;
+}
+
+String CommentsFrame::toString() const
+{
+  return d->text;
+}
+
+ByteVector CommentsFrame::language() const
+{
+  return d->language;
+}
+
+String CommentsFrame::description() const
+{
+  return d->description;
+}
+
+String CommentsFrame::text() const
+{
+  return d->text;
+}
+
+void CommentsFrame::setLanguage(const ByteVector &languageEncoding)
+{
+  d->language = languageEncoding.mid(0, 3);
+}
+
+void CommentsFrame::setDescription(const String &s)
+{
+  d->description = s;
+}
+
+void CommentsFrame::setText(const String &s)
+{
+  d->text = s;
+}
+
+String::Type CommentsFrame::textEncoding() const
+{
+  return d->textEncoding;
+}
+
+void CommentsFrame::setTextEncoding(String::Type encoding)
+{
+  d->textEncoding = encoding;
+}
+
+CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
+{
+  ID3v2::FrameList comments = tag->frameList("COMM");
+
+  for(ID3v2::FrameList::ConstIterator it = comments.begin();
+      it != comments.end();
+      ++it)
+  {
+    CommentsFrame *frame = dynamic_cast<CommentsFrame *>(*it);
+    if(frame && frame->description() == d)
+      return frame;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void CommentsFrame::parseFields(const ByteVector &data)
+{
+  if(data.size() < 5) {
+    debug("A comment frame must contain at least 5 bytes.");
+    return;
+  }
+
+  d->textEncoding = String::Type(data[0]);
+  d->language = data.mid(1, 3);
+
+  int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+
+  ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+  if(l.size() == 2) {
+    d->description = String(l.front(), d->textEncoding);
+    d->text = String(l.back(), d->textEncoding);
+  }
+}
+
+ByteVector CommentsFrame::renderFields() const
+{
+  ByteVector v;
+
+  String::Type encoding = d->textEncoding;
+
+  encoding = checkEncoding(d->description, encoding);
+  encoding = checkEncoding(d->text, encoding);
+
+  v.append(char(encoding));
+  v.append(d->language.size() == 3 ? d->language : "XXX");
+  v.append(d->description.data(encoding));
+  v.append(textDelimiter(encoding));
+  v.append(d->text.data(encoding));
+
+  return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new CommentsFramePrivate();
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/commentsframe.h b/src/taglib/mpeg/id3v2/frames/commentsframe.h
new file mode 100644 (file)
index 0000000..d02015d
--- /dev/null
@@ -0,0 +1,168 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_COMMENTSFRAME_H
+#define TAGLIB_COMMENTSFRAME_H
+
+#include "id3v2frame.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! An implementation of ID3v2 comments
+
+    /*!
+     * This implements the ID3v2 comment format.  An ID3v2 comment concists of
+     * a language encoding, a description and a single text field.
+     */
+
+    class TAGLIB_EXPORT CommentsFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Construct an empty comment frame that will use the text encoding
+       * \a encoding.
+       */
+      explicit CommentsFrame(String::Type encoding = String::Latin1);
+
+      /*!
+       * Construct a comment based on the data in \a data.
+       */
+      explicit CommentsFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this CommentFrame instance.
+       */
+      virtual ~CommentsFrame();
+
+      /*!
+       * Returns the text of this comment.
+       *
+       * \see text()
+       */
+      virtual String toString() const;
+
+      /*!
+       * Returns the language encoding as a 3 byte encoding as specified by
+       * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>.
+       *
+       * \note Most taggers simply ignore this value.
+       *
+       * \see setLanguage()
+       */
+      ByteVector language() const;
+
+      /*!
+       * Returns the description of this comment.
+       *
+       * \note Most taggers simply ignore this value.
+       *
+       * \see setDescription()
+       */
+      String description() const;
+
+      /*!
+       * Returns the text of this comment.
+       *
+       * \see setText()
+       */
+      String text() const;
+
+      /*!
+       * Set the language using the 3 byte language code from
+       * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to
+       * \a languageCode.
+       *
+       * \see language()
+       */
+      void setLanguage(const ByteVector &languageCode);
+
+      /*!
+       * Sets the description of the comment to \a s.
+       *
+       * \see decription()
+       */
+      void setDescription(const String &s);
+
+      /*!
+       * Sets the text portion of the comment to \a s.
+       *
+       * \see text()
+       */
+      virtual void setText(const String &s);
+
+      /*!
+       * Returns the text encoding that will be used in rendering this frame.
+       * This defaults to the type that was either specified in the constructor
+       * or read from the frame when parsed.
+       *
+       * \see setTextEncoding()
+       * \see render()
+       */
+      String::Type textEncoding() const;
+
+      /*!
+       * Sets the text encoding to be used when rendering this frame to
+       * \a encoding.
+       *
+       * \see textEncoding()
+       * \see render()
+       */
+      void setTextEncoding(String::Type encoding);
+
+      /*!
+       * Comments each have a unique description.  This searches for a comment
+       * frame with the decription \a d and returns a pointer to it.  If no
+       * frame is found that matches the given description null is returned.
+       *
+       * \see description()
+       */
+      static CommentsFrame *findByDescription(const Tag *tag, const String &d);
+
+    protected:
+      // Reimplementations.
+
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      CommentsFrame(const ByteVector &data, Header *h);
+      CommentsFrame(const CommentsFrame &);
+      CommentsFrame &operator=(const CommentsFrame &);
+
+      class CommentsFramePrivate;
+      CommentsFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp b/src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
new file mode 100644 (file)
index 0000000..58b7b63
--- /dev/null
@@ -0,0 +1,176 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+    copyright            : (C) 2006 by Aaron VonderHaar
+    email                : avh4@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tdebug.h>
+
+#include "generalencapsulatedobjectframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFramePrivate
+{
+public:
+  GeneralEncapsulatedObjectFramePrivate() : textEncoding(String::Latin1) {}
+
+  String::Type textEncoding;
+  String mimeType;
+  String fileName;
+  String description;
+  ByteVector data;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB")
+{
+    d = new GeneralEncapsulatedObjectFramePrivate;
+}
+
+GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data)
+{
+  d = new GeneralEncapsulatedObjectFramePrivate;
+  setData(data);
+}
+
+GeneralEncapsulatedObjectFrame::~GeneralEncapsulatedObjectFrame()
+{
+  delete d;
+}
+
+String GeneralEncapsulatedObjectFrame::toString() const
+{
+  String text = "[" + d->mimeType + "]";
+
+  if(!d->fileName.isEmpty())
+    text += " " + d->fileName;
+
+  if(!d->description.isEmpty())
+    text += " \"" + d->description + "\"";
+
+  return text;
+}
+
+String::Type GeneralEncapsulatedObjectFrame::textEncoding() const
+{
+  return d->textEncoding;
+}
+
+void GeneralEncapsulatedObjectFrame::setTextEncoding(String::Type encoding)
+{
+  d->textEncoding = encoding;
+}
+
+String GeneralEncapsulatedObjectFrame::mimeType() const
+{
+  return d->mimeType;
+}
+
+void GeneralEncapsulatedObjectFrame::setMimeType(const String &type)
+{
+  d->mimeType = type;
+}
+
+String GeneralEncapsulatedObjectFrame::fileName() const
+{
+  return d->fileName;
+}
+
+void GeneralEncapsulatedObjectFrame::setFileName(const String &name)
+{
+  d->fileName = name;
+}
+
+String GeneralEncapsulatedObjectFrame::description() const
+{
+  return d->description;
+}
+
+void GeneralEncapsulatedObjectFrame::setDescription(const String &desc)
+{
+  d->description = desc;
+}
+
+ByteVector GeneralEncapsulatedObjectFrame::object() const
+{
+  return d->data;
+}
+
+void GeneralEncapsulatedObjectFrame::setObject(const ByteVector &data)
+{
+  d->data = data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data)
+{
+  if(data.size() < 4) {
+    debug("An object frame must contain at least 4 bytes.");
+    return;
+  }
+
+  d->textEncoding = String::Type(data[0]);
+
+  int pos = 1;
+
+  d->mimeType = readStringField(data, String::Latin1, &pos);
+  d->fileName = readStringField(data, d->textEncoding, &pos);
+  d->description = readStringField(data, d->textEncoding, &pos);
+
+  d->data = data.mid(pos);
+}
+
+ByteVector GeneralEncapsulatedObjectFrame::renderFields() const
+{
+  ByteVector data;
+
+  data.append(char(d->textEncoding));
+  data.append(d->mimeType.data(String::Latin1));
+  data.append(textDelimiter(String::Latin1));
+  data.append(d->fileName.data(d->textEncoding));
+  data.append(textDelimiter(d->textEncoding));
+  data.append(d->description.data(d->textEncoding));
+  data.append(textDelimiter(d->textEncoding));
+  data.append(d->data);
+
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new GeneralEncapsulatedObjectFramePrivate;
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h b/src/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h
new file mode 100644 (file)
index 0000000..ce462c6
--- /dev/null
@@ -0,0 +1,178 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+    copyright            : (C) 2006 by Aaron VonderHaar
+    email                : avh4@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_GENERALENCAPSULATEDOBJECT_H
+#define TAGLIB_GENERALENCAPSULATEDOBJECT_H
+
+#include "id3v2frame.h"
+#include "id3v2header.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! An ID3v2 general encapsulated object frame implementation
+
+    /*!
+     * This is an implementation of ID3v2 general encapsulated objects.
+     * Arbitrary binary data may be included in tags, stored in GEOB frames.
+     * There may be multiple GEOB frames in a single tag.  Each GEOB it
+     * labelled with a content description (which may be blank), a required
+     * mime-type, and a file name (may be blank).  The content description
+     * uniquely identifies the GEOB frame in the tag.
+     */
+
+    class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+
+      /*!
+       * Constructs an empty object frame.  The description, file name and text
+       * encoding should be set manually.
+       */
+      GeneralEncapsulatedObjectFrame();
+
+      /*!
+       * Constructs a GeneralEncapsulatedObjectFrame frame based on \a data.
+       *
+       * \warning This is \em not data for the encapsulated object, for that use
+       * setObject().  This constructor is used when reading the frame from the
+       * disk.
+       */
+      explicit GeneralEncapsulatedObjectFrame(const ByteVector &data);
+
+      /*!
+       * Destroys the GeneralEncapsulatedObjectFrame instance.
+       */
+      virtual ~GeneralEncapsulatedObjectFrame();
+
+      /*!
+       * Returns a string containing the description, file name and mime-type
+       */
+      virtual String toString() const;
+
+      /*!
+       * Returns the text encoding used for the description and file name.
+       *
+       * \see setTextEncoding()
+       * \see description()
+       * \see fileName()
+       */
+      String::Type textEncoding() const;
+
+      /*!
+       * Set the text encoding used for the description and file name.
+       *
+       * \see description()
+       * \see fileName()
+       */
+      void setTextEncoding(String::Type encoding);
+
+      /*!
+       * Returns the mime type of the object.
+       */
+      String mimeType() const;
+
+      /*!
+       * Sets the mime type of the object.
+       */
+      void setMimeType(const String &type);
+
+      /*!
+       * Returns the file name of the object.
+       *
+       * \see setFileName()
+       */
+      String fileName() const;
+
+      /*!
+       * Sets the file name for the object.
+       *
+       * \see fileName()
+       */
+      void setFileName(const String &name);
+
+      /*!
+       * Returns the content description of the object.
+       *
+       * \see setDescription()
+       * \see textEncoding()
+       * \see setTextEncoding()
+       */
+
+      String description() const;
+
+      /*!
+       * Sets the content description of the object to \a desc.
+       *
+       * \see description()
+       * \see textEncoding()
+       * \see setTextEncoding()
+       */
+
+      void setDescription(const String &desc);
+
+      /*!
+       * Returns the object data as a ByteVector.
+       *
+       * \note ByteVector has a data() method that returns a const char * which
+       * should make it easy to export this data to external programs.
+       *
+       * \see setObject()
+       * \see mimeType()
+       */
+      ByteVector object() const;
+
+      /*!
+       * Sets the object data to \a data.  \a data should be of the type specified in
+       * this frame's mime-type specification.
+       *
+       * \see object()
+       * \see mimeType()
+       * \see setMimeType()
+       */
+      void setObject(const ByteVector &object);
+
+    protected:
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h);
+      GeneralEncapsulatedObjectFrame(const GeneralEncapsulatedObjectFrame &);
+      GeneralEncapsulatedObjectFrame &operator=(const GeneralEncapsulatedObjectFrame &);
+
+      class GeneralEncapsulatedObjectFramePrivate;
+      GeneralEncapsulatedObjectFramePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/popularimeterframe.cpp b/src/taglib/mpeg/id3v2/frames/popularimeterframe.cpp
new file mode 100644 (file)
index 0000000..e65cbe0
--- /dev/null
@@ -0,0 +1,137 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Lukas Lalinsky
+    email                : lalinsky@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tdebug.h>
+
+#include "popularimeterframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class PopularimeterFrame::PopularimeterFramePrivate
+{
+public:
+  PopularimeterFramePrivate() : rating(0), counter(0) {}
+  String email;
+  int rating;
+  TagLib::uint counter;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+PopularimeterFrame::PopularimeterFrame() : Frame("POPM")
+{
+  d = new PopularimeterFramePrivate;
+}
+
+PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data)
+{
+  d = new PopularimeterFramePrivate;
+  setData(data);
+}
+
+PopularimeterFrame::~PopularimeterFrame()
+{
+  delete d;
+}
+
+String PopularimeterFrame::toString() const
+{
+  return d->email + " rating=" + String::number(d->rating) + " counter=" + String::number(d->counter);
+}
+
+String PopularimeterFrame::email() const
+{
+  return d->email;
+}
+
+void PopularimeterFrame::setEmail(const String &s)
+{
+  d->email = s;
+}
+
+int PopularimeterFrame::rating() const
+{
+  return d->rating;
+}
+
+void PopularimeterFrame::setRating(int s)
+{
+  d->rating = s;
+}
+
+TagLib::uint PopularimeterFrame::counter() const
+{
+  return d->counter;
+}
+
+void PopularimeterFrame::setCounter(TagLib::uint s)
+{
+  d->counter = s;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void PopularimeterFrame::parseFields(const ByteVector &data)
+{
+  int pos = 0, size = int(data.size());
+
+  d->email = readStringField(data, String::Latin1, &pos);
+
+  d->rating = 0;
+  d->counter = 0;
+  if(pos < size) {
+    d->rating = (unsigned char)(data[pos++]);
+    if(pos < size) {
+      d->counter = data.mid(pos, 4).toUInt();
+    }
+  }
+}
+
+ByteVector PopularimeterFrame::renderFields() const
+{
+  ByteVector data;
+
+  data.append(d->email.data(String::Latin1));
+  data.append(textDelimiter(String::Latin1));
+  data.append(char(d->rating));
+  data.append(ByteVector::fromUInt(d->counter));
+
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new PopularimeterFramePrivate;
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/popularimeterframe.h b/src/taglib/mpeg/id3v2/frames/popularimeterframe.h
new file mode 100644 (file)
index 0000000..b1010df
--- /dev/null
@@ -0,0 +1,132 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Lukas Lalinsky
+    email                : lalinsky@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_POPULARIMETERFRAME_H
+#define TAGLIB_POPULARIMETERFRAME_H
+
+#include "id3v2frame.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! An implementation of ID3v2 "popularimeter"
+
+    /*!
+     * This implements the ID3v2 popularimeter (POPM frame).  It concists of
+     * an email, a rating and an optional counter.
+     */
+
+    class TAGLIB_EXPORT PopularimeterFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Construct an empty popularimeter frame.
+       */
+      explicit PopularimeterFrame();
+
+      /*!
+       * Construct a popularimeter based on the data in \a data.
+       */
+      explicit PopularimeterFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this PopularimeterFrame instance.
+       */
+      virtual ~PopularimeterFrame();
+
+      /*!
+       * Returns the text of this popularimeter.
+       *
+       * \see text()
+       */
+      virtual String toString() const;
+
+      /*!
+       * Returns the email.
+       *
+       * \see setEmail()
+       */
+      String email() const;
+
+      /*!
+       * Set the email.
+       *
+       * \see email()
+       */
+      void setEmail(const String &email);
+
+      /*!
+       * Returns the rating.
+       *
+       * \see setRating()
+       */
+      int rating() const;
+
+      /*!
+       * Set the rating.
+       *
+       * \see rating()
+       */
+      void setRating(int rating);
+
+      /*!
+       * Returns the counter.
+       *
+       * \see setCounter()
+       */
+      uint counter() const;
+
+      /*!
+       * Set the counter.
+       *
+       * \see counter()
+       */
+      void setCounter(uint counter);
+
+    protected:
+      // Reimplementations.
+
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      PopularimeterFrame(const ByteVector &data, Header *h);
+      PopularimeterFrame(const PopularimeterFrame &);
+      PopularimeterFrame &operator=(const PopularimeterFrame &);
+
+      class PopularimeterFramePrivate;
+      PopularimeterFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/privateframe.cpp b/src/taglib/mpeg/id3v2/frames/privateframe.cpp
new file mode 100644 (file)
index 0000000..9491561
--- /dev/null
@@ -0,0 +1,128 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Serkan Kalyoncu
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevectorlist.h>
+#include <id3v2tag.h>
+#include <tdebug.h>
+
+#include "privateframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+
+class PrivateFrame::PrivateFramePrivate
+{
+public:
+  ByteVector data;
+  String owner;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+PrivateFrame::PrivateFrame() : Frame("PRIV")
+{
+  d = new PrivateFramePrivate;
+}
+
+PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data)
+{
+  d = new PrivateFramePrivate;
+  setData(data);
+}
+
+PrivateFrame::~PrivateFrame()
+{
+  delete d;
+}
+
+String PrivateFrame::toString() const
+{
+  return d->owner;
+}
+
+String PrivateFrame::owner() const
+{
+  return d->owner;
+}
+
+ByteVector PrivateFrame::data() const
+{
+  return d->data;
+}
+
+void PrivateFrame::setOwner(const String &s)
+{
+  d->owner = s;
+}
+
+void PrivateFrame::setData(const ByteVector & data)
+{
+  d->data = data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void PrivateFrame::parseFields(const ByteVector &data)
+{
+  if(data.size() < 2) {
+    debug("A private frame must contain at least 2 bytes.");
+    return;
+  }
+
+  // Owner identifier is assumed to be Latin1
+  
+  const int byteAlign =  1;
+  const int endOfOwner = data.find(textDelimiter(String::Latin1), 0, byteAlign);
+
+  d->owner =  String(data.mid(0, endOfOwner));
+  d->data = data.mid(endOfOwner + 1);
+}
+
+ByteVector PrivateFrame::renderFields() const
+{
+  ByteVector v;
+
+  v.append(d->owner.data(String::Latin1));
+  v.append(textDelimiter(String::Latin1));
+  v.append(d->data);
+
+  return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new PrivateFramePrivate();
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/privateframe.h b/src/taglib/mpeg/id3v2/frames/privateframe.h
new file mode 100644 (file)
index 0000000..866a6d4
--- /dev/null
@@ -0,0 +1,111 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Serkan Kalyoncu
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_PRIVATEFRAME_H
+#define TAGLIB_PRIVATEFRAME_H
+
+#include "id3v2frame.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! An implementation of ID3v2 privateframe
+
+    class TAGLIB_EXPORT PrivateFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Construct an empty private frame.
+       */
+      PrivateFrame();
+
+      /*!
+       * Construct a private frame based on the data in \a data.
+       *
+       * \note This is the constructor used when parsing the frame from a file.
+       */
+      explicit PrivateFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this private frame instance.
+       */
+      virtual ~PrivateFrame();
+
+      /*!
+       * Returns the text of this private frame, currently just the owner.
+       *
+       * \see text()
+       */
+      virtual String toString() const;
+
+      /*!
+       * \return The owner of the private frame.
+       * \note This should contain an email address or link to a website.
+       */
+      String owner() const;
+
+      /*!
+       *
+       */
+      ByteVector data() const;
+
+      /*!
+       * Sets the owner of the frame to \a s.
+       * \note This should contain an email address or link to a website.
+       */
+      void setOwner(const String &s);
+
+      /*!
+       *
+       */
+      void setData(const ByteVector &v);
+
+    protected:
+      // Reimplementations.
+
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      PrivateFrame(const ByteVector &data, Header *h);
+
+      PrivateFrame(const PrivateFrame &);
+      PrivateFrame &operator=(const PrivateFrame &);
+
+      class PrivateFramePrivate;
+      PrivateFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp b/src/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp
new file mode 100644 (file)
index 0000000..8495197
--- /dev/null
@@ -0,0 +1,236 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tdebug.h>
+#include <tmap.h>
+
+#include "relativevolumeframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+static inline int bitsToBytes(int i)
+{
+  return i % 8 == 0 ? i / 8 : (i - i % 8) / 8 + 1;
+}
+
+struct ChannelData
+{
+  ChannelData() : channelType(RelativeVolumeFrame::Other), volumeAdjustment(0) {}
+
+  RelativeVolumeFrame::ChannelType channelType;
+  short volumeAdjustment;
+  RelativeVolumeFrame::PeakVolume peakVolume;
+};
+
+class RelativeVolumeFrame::RelativeVolumeFramePrivate
+{
+public:
+  String identification;
+  Map<ChannelType, ChannelData> channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2")
+{
+  d = new RelativeVolumeFramePrivate;
+}
+
+RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data)
+{
+  d = new RelativeVolumeFramePrivate;
+  setData(data);
+}
+
+RelativeVolumeFrame::~RelativeVolumeFrame()
+{
+  delete d;
+}
+
+String RelativeVolumeFrame::toString() const
+{
+  return d->identification;
+}
+
+List<RelativeVolumeFrame::ChannelType> RelativeVolumeFrame::channels() const
+{
+  List<ChannelType> l;
+
+  Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin();
+  for(; it != d->channels.end(); ++it)
+    l.append((*it).first);
+
+  return l;
+}
+
+// deprecated
+
+RelativeVolumeFrame::ChannelType RelativeVolumeFrame::channelType() const
+{
+  return MasterVolume;
+}
+
+// deprecated
+
+void RelativeVolumeFrame::setChannelType(ChannelType)
+{
+
+}
+
+short RelativeVolumeFrame::volumeAdjustmentIndex(ChannelType type) const
+{
+  return d->channels.contains(type) ? d->channels[type].volumeAdjustment : 0;
+}
+
+short RelativeVolumeFrame::volumeAdjustmentIndex() const
+{
+  return volumeAdjustmentIndex(MasterVolume);
+}
+
+void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index, ChannelType type)
+{
+  d->channels[type].volumeAdjustment = index;
+}
+
+void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index)
+{
+  setVolumeAdjustmentIndex(index, MasterVolume);
+}
+
+float RelativeVolumeFrame::volumeAdjustment(ChannelType type) const
+{
+  return d->channels.contains(type) ? float(d->channels[type].volumeAdjustment) / float(512) : 0;
+}
+
+float RelativeVolumeFrame::volumeAdjustment() const
+{
+  return volumeAdjustment(MasterVolume);
+}
+
+void RelativeVolumeFrame::setVolumeAdjustment(float adjustment, ChannelType type)
+{
+  d->channels[type].volumeAdjustment = short(adjustment * float(512));
+}
+
+void RelativeVolumeFrame::setVolumeAdjustment(float adjustment)
+{
+  setVolumeAdjustment(adjustment, MasterVolume);
+}
+
+RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume(ChannelType type) const
+{
+  return d->channels.contains(type) ? d->channels[type].peakVolume : PeakVolume();
+}
+
+RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume() const
+{
+  return peakVolume(MasterVolume);
+}
+
+void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak, ChannelType type)
+{
+  d->channels[type].peakVolume = peak;
+}
+
+void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak)
+{
+  setPeakVolume(peak, MasterVolume);
+}
+
+String RelativeVolumeFrame::identification() const
+{
+  return d->identification;
+}
+
+void RelativeVolumeFrame::setIdentification(const String &s)
+{
+  d->identification = s;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void RelativeVolumeFrame::parseFields(const ByteVector &data)
+{
+  int pos = 0;
+  d->identification = readStringField(data, String::Latin1, &pos);
+
+  // Each channel is at least 4 bytes.
+
+  while(pos <= (int)data.size() - 4) {
+
+
+    ChannelType type = ChannelType(data[pos]);
+    pos += 1;
+
+    ChannelData &channel = d->channels[type];
+
+    channel.volumeAdjustment = data.mid(pos, 2).toShort();
+    pos += 2;
+
+    channel.peakVolume.bitsRepresentingPeak = data[pos];
+    pos += 1;
+
+    int bytes = bitsToBytes(channel.peakVolume.bitsRepresentingPeak);
+    channel.peakVolume.peakVolume = data.mid(pos, bytes);
+    pos += bytes;
+  }
+}
+
+ByteVector RelativeVolumeFrame::renderFields() const
+{
+  ByteVector data;
+
+  data.append(d->identification.data(String::Latin1));
+  data.append(textDelimiter(String::Latin1));
+
+  Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin();
+
+  for(; it != d->channels.end(); ++it) {
+    ChannelType type = (*it).first;
+    const ChannelData &channel = (*it).second;
+
+    data.append(char(type));
+    data.append(ByteVector::fromShort(channel.volumeAdjustment));
+    data.append(char(channel.peakVolume.bitsRepresentingPeak));
+    data.append(channel.peakVolume.peakVolume);
+  }
+
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new RelativeVolumeFramePrivate;
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/relativevolumeframe.h b/src/taglib/mpeg/id3v2/frames/relativevolumeframe.h
new file mode 100644 (file)
index 0000000..189199f
--- /dev/null
@@ -0,0 +1,274 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_RELATIVEVOLUMEFRAME_H
+#define TAGLIB_RELATIVEVOLUMEFRAME_H
+
+#include "tlist.h"
+#include "id3v2frame.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! An ID3v2 relative volume adjustment frame implementation
+
+    /*!
+     * This is an implementation of ID3v2 relative volume adjustment.  The
+     * presence of this frame makes it possible to specify an increase in volume
+     * for an audio file or specific audio tracks in that file.
+     *
+     * Multiple relative volume adjustment frames may be present in the tag
+     * each with a unique identification and describing volume adjustment for
+     * different channel types.
+     */
+
+    class TAGLIB_EXPORT RelativeVolumeFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+
+      /*!
+       * This indicates the type of volume adjustment that should be applied.
+       */
+      enum ChannelType {
+        //! A type not enumerated below
+        Other        = 0x00,
+        //! The master volume for the track
+        MasterVolume = 0x01,
+        //! The front right audio channel
+        FrontRight   = 0x02,
+        //! The front left audio channel
+        FrontLeft    = 0x03,
+        //! The back right audio channel
+        BackRight    = 0x04,
+        //! The back left audio channel
+        BackLeft     = 0x05,
+        //! The front center audio channel
+        FrontCentre  = 0x06,
+        //! The back center audio channel
+        BackCentre   = 0x07,
+        //! The subwoofer audio channel
+        Subwoofer    = 0x08
+      };
+
+      //! Struct that stores the relevant values for ID3v2 peak volume
+
+      /*!
+       * The peak volume is described as a series of bits that is padded to fill
+       * a block of bytes.  These two values should always be updated in tandem.
+       */
+      struct PeakVolume
+      {
+        /*!
+         * Constructs an empty peak volume description.
+         */
+        PeakVolume() : bitsRepresentingPeak(0) {}
+        /*!
+         * The number of bits (in the range of 0 to 255) used to describe the
+         * peak volume.
+         */
+        unsigned char bitsRepresentingPeak;
+        /*!
+         * The array of bits (represented as a series of bytes) used to describe
+         * the peak volume.
+         */
+        ByteVector peakVolume;
+      };
+
+      /*!
+       * Constructs a RelativeVolumeFrame.  The relevant data should be set
+       * manually.
+       */
+      RelativeVolumeFrame();
+
+      /*!
+       * Constructs a RelativeVolumeFrame based on the contents of \a data.
+       */
+      RelativeVolumeFrame(const ByteVector &data);
+
+      /*!
+       * Destroys the RelativeVolumeFrame instance.
+       */
+      virtual ~RelativeVolumeFrame();
+
+      /*!
+       * Returns the frame's identification.
+       *
+       * \see identification()
+       */
+      virtual String toString() const;
+
+      /*!
+       * Returns a list of channels with information currently in the frame.
+       */
+      List<ChannelType> channels() const;
+
+      /*!
+       * \deprecated Always returns master volume.
+       */
+      ChannelType channelType() const;
+
+      /*!
+       * \deprecated This method no longer has any effect.
+       */
+      void setChannelType(ChannelType t);
+
+      /*
+       * There was a terrible API goof here, and while this can't be changed to
+       * the way it appears below for binary compaibility reasons, let's at
+       * least pretend that it looks clean.
+       */
+
+#ifdef DOXYGEN
+
+      /*!
+       * Returns the relative volume adjustment "index".  As indicated by the
+       * ID3v2 standard this is a 16-bit signed integer that reflects the
+       * decibils of adjustment when divided by 512.
+       *
+       * This defaults to returning the value for the master volume channel if
+       * available and returns 0 if the specified channel does not exist.
+       *
+       * \see setVolumeAdjustmentIndex()
+       * \see volumeAjustment()
+       */
+      short volumeAdjustmentIndex(ChannelType type = MasterVolume) const;
+
+      /*!
+       * Set the volume adjustment to \a index.  As indicated by the ID3v2
+       * standard this is a 16-bit signed integer that reflects the decibils of
+       * adjustment when divided by 512.
+       *
+       * By default this sets the value for the master volume.
+       *
+       * \see volumeAdjustmentIndex()
+       * \see setVolumeAjustment()
+       */
+      void setVolumeAdjustmentIndex(short index, ChannelType type = MasterVolume);
+
+      /*!
+       * Returns the relative volume adjustment in decibels.
+       *
+       * \note Because this is actually stored internally as an "index" to this
+       * value the value returned by this method may not be identical to the
+       * value set using setVolumeAdjustment().
+       *
+       * This defaults to returning the value for the master volume channel if
+       * available and returns 0 if the specified channel does not exist.
+       *
+       * \see setVolumeAdjustment()
+       * \see volumeAdjustmentIndex()
+       */
+      float volumeAdjustment(ChannelType type = MasterVolume) const;
+
+      /*!
+       * Set the relative volume adjustment in decibels to \a adjustment.
+       *
+       * By default this sets the value for the master volume.
+       *
+       * \note Because this is actually stored internally as an "index" to this
+       * value the value set by this method may not be identical to the one
+       * returned by volumeAdjustment().
+       *
+       * \see setVolumeAdjustment()
+       * \see volumeAdjustmentIndex()
+       */
+      void setVolumeAdjustment(float adjustment, ChannelType type = MasterVolume);
+
+      /*!
+       * Returns the peak volume (represented as a length and a string of bits).
+       *
+       * This defaults to returning the value for the master volume channel if
+       * available and returns 0 if the specified channel does not exist.
+       *
+       * \see setPeakVolume()
+       */
+      PeakVolume peakVolume(ChannelType type = MasterVolume) const;
+
+      /*!
+       * Sets the peak volume to \a peak.
+       *
+       * By default this sets the value for the master volume.
+       *
+       * \see peakVolume()
+       */
+      void setPeakVolume(const PeakVolume &peak, ChannelType type = MasterVolume);
+
+#else
+
+      // BIC: Combine each of the following pairs of functions (or maybe just
+      // rework this junk altogether).
+
+      short volumeAdjustmentIndex(ChannelType type) const;
+      short volumeAdjustmentIndex() const;
+
+      void setVolumeAdjustmentIndex(short index, ChannelType type);
+      void setVolumeAdjustmentIndex(short index);
+
+      float volumeAdjustment(ChannelType type) const;
+      float volumeAdjustment() const;
+
+      void setVolumeAdjustment(float adjustment, ChannelType type);
+      void setVolumeAdjustment(float adjustment);
+
+      PeakVolume peakVolume(ChannelType type) const;
+      PeakVolume peakVolume() const;
+
+      void setPeakVolume(const PeakVolume &peak, ChannelType type);
+      void setPeakVolume(const PeakVolume &peak);
+
+#endif
+
+      /*!
+       * Returns the identification for this frame.
+       */
+      String identification() const;
+
+      /*!
+       * Sets the identification of the frame to \a s. The string
+       * is used to identify the situation and/or device where this
+       * adjustment should apply.
+       */
+      void setIdentification(const String &s);
+
+    protected:
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      RelativeVolumeFrame(const ByteVector &data, Header *h);
+      RelativeVolumeFrame(const RelativeVolumeFrame &);
+      RelativeVolumeFrame &operator=(const RelativeVolumeFrame &);
+
+      class RelativeVolumeFramePrivate;
+      RelativeVolumeFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/src/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
new file mode 100644 (file)
index 0000000..7d3b4ff
--- /dev/null
@@ -0,0 +1,271 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevectorlist.h>
+#include <id3v2tag.h>
+
+#include "textidentificationframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class TextIdentificationFrame::TextIdentificationFramePrivate
+{
+public:
+  TextIdentificationFramePrivate() : textEncoding(String::Latin1) {}
+  String::Type textEncoding;
+  StringList fieldList;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// TextIdentificationFrame public members
+////////////////////////////////////////////////////////////////////////////////
+
+TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) :
+  Frame(type)
+{
+  d = new TextIdentificationFramePrivate;
+  d->textEncoding = encoding;
+}
+
+TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) :
+  Frame(data)
+{
+  d = new TextIdentificationFramePrivate;
+  setData(data);
+}
+
+TextIdentificationFrame::~TextIdentificationFrame()
+{
+  delete d;
+}
+
+void TextIdentificationFrame::setText(const StringList &l)
+{
+  d->fieldList = l;
+}
+
+void TextIdentificationFrame::setText(const String &s)
+{
+  d->fieldList = s;
+}
+
+String TextIdentificationFrame::toString() const
+{
+  return d->fieldList.toString();
+}
+
+StringList TextIdentificationFrame::fieldList() const
+{
+  return d->fieldList;
+}
+
+String::Type TextIdentificationFrame::textEncoding() const
+{
+  return d->textEncoding;
+}
+
+void TextIdentificationFrame::setTextEncoding(String::Type encoding)
+{
+  d->textEncoding = encoding;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TextIdentificationFrame protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void TextIdentificationFrame::parseFields(const ByteVector &data)
+{
+  // Don't try to parse invalid frames
+
+  if(data.size() < 2)
+    return;
+
+  // read the string data type (the first byte of the field data)
+
+  d->textEncoding = String::Type(data[0]);
+
+  // split the byte array into chunks based on the string type (two byte delimiter
+  // for unicode encodings)
+
+  int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+
+  // build a small counter to strip nulls off the end of the field
+
+  int dataLength = data.size() - 1;
+
+  while(dataLength > 0 && data[dataLength] == 0)
+    dataLength--;
+
+  while(dataLength % byteAlign != 0)
+    dataLength++;
+
+  ByteVectorList l = ByteVectorList::split(data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign);
+
+  d->fieldList.clear();
+
+  // append those split values to the list and make sure that the new string's
+  // type is the same specified for this frame
+
+  for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
+    if(!(*it).isEmpty()) {
+      String s(*it, d->textEncoding);
+      d->fieldList.append(s);
+    }
+  }
+}
+
+ByteVector TextIdentificationFrame::renderFields() const
+{
+  String::Type encoding = checkEncoding(d->fieldList, d->textEncoding);
+
+  ByteVector v;
+
+  v.append(char(encoding));
+
+  for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+    // Since the field list is null delimited, if this is not the first
+    // element in the list, append the appropriate delimiter for this
+    // encoding.
+
+    if(it != d->fieldList.begin())
+      v.append(textDelimiter(encoding));
+
+    v.append((*it).data(encoding));
+  }
+
+  return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TextIdentificationFrame private members
+////////////////////////////////////////////////////////////////////////////////
+
+TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new TextIdentificationFramePrivate;
+  parseFields(fieldData(data));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// UserTextIdentificationFrame public members
+////////////////////////////////////////////////////////////////////////////////
+
+UserTextIdentificationFrame::UserTextIdentificationFrame(String::Type encoding) :
+  TextIdentificationFrame("TXXX", encoding),
+  d(0)
+{
+  StringList l;
+  l.append(String::null);
+  l.append(String::null);
+  setText(l);
+}
+
+
+UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data) :
+  TextIdentificationFrame(data)
+{
+  checkFields();
+}
+
+String UserTextIdentificationFrame::toString() const
+{
+  return "[" + description() + "] " + fieldList().toString();
+}
+
+String UserTextIdentificationFrame::description() const
+{
+  return !TextIdentificationFrame::fieldList().isEmpty()
+    ? TextIdentificationFrame::fieldList().front()
+    : String::null;
+}
+
+StringList UserTextIdentificationFrame::fieldList() const
+{
+  // TODO: remove this function
+
+  return TextIdentificationFrame::fieldList();
+}
+
+void UserTextIdentificationFrame::setText(const String &text)
+{
+  if(description().isEmpty())
+    setDescription(String::null);
+
+  TextIdentificationFrame::setText(StringList(description()).append(text));
+}
+
+void UserTextIdentificationFrame::setText(const StringList &fields)
+{
+  if(description().isEmpty())
+    setDescription(String::null);
+
+  TextIdentificationFrame::setText(StringList(description()).append(fields));
+}
+
+void UserTextIdentificationFrame::setDescription(const String &s)
+{
+  StringList l = fieldList();
+
+  if(l.isEmpty())
+    l.append(s);
+  else
+    l[0] = s;
+
+  TextIdentificationFrame::setText(l);
+}
+
+UserTextIdentificationFrame *UserTextIdentificationFrame::find(
+  ID3v2::Tag *tag, const String &description) // static
+{
+  FrameList l = tag->frameList("TXXX");
+  for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) {
+    UserTextIdentificationFrame *f = dynamic_cast<UserTextIdentificationFrame *>(*it);
+    if(f && f->description() == description)
+      return f;
+  }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// UserTextIdentificationFrame private members
+////////////////////////////////////////////////////////////////////////////////
+
+UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data, Header *h) :
+  TextIdentificationFrame(data, h)
+{
+  checkFields();
+}
+
+void UserTextIdentificationFrame::checkFields()
+{
+  int fields = fieldList().size();
+
+  if(fields == 0)
+    setDescription(String::null);
+  if(fields <= 1)
+    setText(String::null);
+}
diff --git a/src/taglib/mpeg/id3v2/frames/textidentificationframe.h b/src/taglib/mpeg/id3v2/frames/textidentificationframe.h
new file mode 100644 (file)
index 0000000..eccebeb
--- /dev/null
@@ -0,0 +1,258 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_TEXTIDENTIFICATIONFRAME_H
+#define TAGLIB_TEXTIDENTIFICATIONFRAME_H
+
+#include "tstringlist.h"
+#include "taglib_export.h"
+
+#include "id3v2frame.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    class Tag;
+
+    //! An ID3v2 text identification frame implementation
+
+    /*!
+     * This is an implementation of the most common type of ID3v2 frame -- text
+     * identification frames.  There are a number of variations on this.  Those
+     * enumerated in the ID3v2.4 standard are:
+     *
+     * <ul>
+     *   <li><b>TALB</b> Album/Movie/Show title</li>
+     *   <li><b>TBPM</b> BPM (beats per minute)</li>
+     *   <li><b>TCOM</b> Composer</li>
+     *   <li><b>TCON</b> Content type</li>
+     *   <li><b>TCOP</b> Copyright message</li>
+     *   <li><b>TDEN</b> Encoding time</li>
+     *   <li><b>TDLY</b> Playlist delay</li>
+     *   <li><b>TDOR</b> Original release time</li>
+     *   <li><b>TDRC</b> Recording time</li>
+     *   <li><b>TDRL</b> Release time</li>
+     *   <li><b>TDTG</b> Tagging time</li>
+     *   <li><b>TENC</b> Encoded by</li>
+     *   <li><b>TEXT</b> Lyricist/Text writer</li>
+     *   <li><b>TFLT</b> File type</li>
+     *   <li><b>TIPL</b> Involved people list</li>
+     *   <li><b>TIT1</b> Content group description</li>
+     *   <li><b>TIT2</b> Title/songname/content description</li>
+     *   <li><b>TIT3</b> Subtitle/Description refinement</li>
+     *   <li><b>TKEY</b> Initial key</li>
+     *   <li><b>TLAN</b> Language(s)</li>
+     *   <li><b>TLEN</b> Length</li>
+     *   <li><b>TMCL</b> Musician credits list</li>
+     *   <li><b>TMED</b> Media type</li>
+     *   <li><b>TMOO</b> Mood</li>
+     *   <li><b>TOAL</b> Original album/movie/show title</li>
+     *   <li><b>TOFN</b> Original filename</li>
+     *   <li><b>TOLY</b> Original lyricist(s)/text writer(s)</li>
+     *   <li><b>TOPE</b> Original artist(s)/performer(s)</li>
+     *   <li><b>TOWN</b> File owner/licensee</li>
+     *   <li><b>TPE1</b> Lead performer(s)/Soloist(s)</li>
+     *   <li><b>TPE2</b> Band/orchestra/accompaniment</li>
+     *   <li><b>TPE3</b> Conductor/performer refinement</li>
+     *   <li><b>TPE4</b> Interpreted, remixed, or otherwise modified by</li>
+     *   <li><b>TPOS</b> Part of a set</li>
+     *   <li><b>TPRO</b> Produced notice</li>
+     *   <li><b>TPUB</b> Publisher</li>
+     *   <li><b>TRCK</b> Track number/Position in set</li>
+     *   <li><b>TRSN</b> Internet radio station name</li>
+     *   <li><b>TRSO</b> Internet radio station owner</li>
+     *   <li><b>TSOA</b> Album sort order</li>
+     *   <li><b>TSOP</b> Performer sort order</li>
+     *   <li><b>TSOT</b> Title sort order</li>
+     *   <li><b>TSRC</b> ISRC (international standard recording code)</li>
+     *   <li><b>TSSE</b> Software/Hardware and settings used for encoding</li>
+     *   <li><b>TSST</b> Set subtitle</li>
+     * </ul>
+     *
+     * The ID3v2 Frames document gives a description of each of these formats
+     * and the expected order of strings in each.  ID3v2::Header::frameID() can
+     * be used to determine the frame type.
+     *
+     * \note If non-Latin1 compatible strings are used with this class, even if
+     * the text encoding is set to Latin1, the frame will be written using UTF8
+     * (with the encoding flag appropriately set in the output).
+     */
+
+    class TAGLIB_EXPORT TextIdentificationFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Construct an empty frame of type \a type.  Uses \a encoding as the
+       * default text encoding.
+       *
+       * \note In this case you must specify the text encoding as it
+       * resolves the ambiguity between constructors.
+       *
+       * \note Please see the note in the class description regarding Latin1.
+       */
+      TextIdentificationFrame(const ByteVector &type, String::Type encoding);
+
+      /*!
+       * This is a dual purpose constructor.  \a data can either be binary data
+       * that should be parsed or (at a minimum) the frame ID.
+       */
+      explicit TextIdentificationFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this TextIdentificationFrame instance.
+       */
+      virtual ~TextIdentificationFrame();
+
+      /*!
+       * Text identification frames are a list of string fields.
+       *
+       * This function will accept either a StringList or a String (using the
+       * StringList constructor that accepts a single String).
+       *
+       * \note This will not change the text encoding of the frame even if the
+       * strings passed in are not of the same encoding.  Please use
+       * setEncoding(s.type()) if you wish to change the encoding of the frame.
+       */
+      void setText(const StringList &l);
+
+      // Reimplementations.
+
+      virtual void setText(const String &s);
+      virtual String toString() const;
+
+      /*!
+       * Returns the text encoding that will be used in rendering this frame.
+       * This defaults to the type that was either specified in the constructor
+       * or read from the frame when parsed.
+       *
+       * \note Please see the note in the class description regarding Latin1.
+       *
+       * \see setTextEncoding()
+       * \see render()
+       */
+      String::Type textEncoding() const;
+
+      /*!
+       * Sets the text encoding to be used when rendering this frame to
+       * \a encoding.
+       *
+       * \note Please see the note in the class description regarding Latin1.
+       *
+       * \see textEncoding()
+       * \see render()
+       */
+      void setTextEncoding(String::Type encoding);
+
+      /*!
+       * Returns a list of the strings in this frame.
+       */
+      StringList fieldList() const;
+
+    protected:
+      // Reimplementations.
+
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      TextIdentificationFrame(const ByteVector &data, Header *h);
+
+    private:
+      TextIdentificationFrame(const TextIdentificationFrame &);
+      TextIdentificationFrame &operator=(const TextIdentificationFrame &);
+
+      class TextIdentificationFramePrivate;
+      TextIdentificationFramePrivate *d;
+    };
+
+    /*!
+     * This is a specialization of text identification frames that allows for
+     * user defined entries.  Each entry has a description in addition to the
+     * normal list of fields that a text identification frame has.
+     *
+     * This description identifies the frame and must be unique.
+     */
+
+    //! An ID3v2 custom text identification frame implementationx
+
+    class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Constructs an empty user defined text identification frame.  For this to be
+       * a useful frame both a description and text must be set.
+       */
+      explicit UserTextIdentificationFrame(String::Type encoding = String::Latin1);
+
+      /*!
+       * Creates a frame based on \a data.
+       */
+      explicit UserTextIdentificationFrame(const ByteVector &data);
+
+      virtual String toString() const;
+
+      /*!
+       * Returns the description for this frame.
+       */
+      String description() const;
+
+      /*!
+       * Sets the description of the frame to \a s.  \a s must be unique.  You can
+       * check for the presence of another user defined text frame of the same type
+       * using find() and testing for null.
+       */
+      void setDescription(const String &s);
+
+      StringList fieldList() const;
+      void setText(const String &text);
+      void setText(const StringList &fields);
+
+      /*!
+       * Searches for the user defined text frame with the description \a description
+       * in \a tag.  This returns null if no matching frames were found.
+       */
+      static UserTextIdentificationFrame *find(Tag *tag, const String &description);
+
+    private:
+      UserTextIdentificationFrame(const ByteVector &data, Header *h);
+      UserTextIdentificationFrame(const TextIdentificationFrame &);
+      UserTextIdentificationFrame &operator=(const UserTextIdentificationFrame &);
+
+      void checkFields();
+
+      class UserTextIdentificationFramePrivate;
+      UserTextIdentificationFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp b/src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp
new file mode 100644 (file)
index 0000000..836982b
--- /dev/null
@@ -0,0 +1,118 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevectorlist.h>
+#include <tdebug.h>
+
+#include "uniquefileidentifierframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class UniqueFileIdentifierFrame::UniqueFileIdentifierFramePrivate
+{
+public:
+  String owner;
+  ByteVector identifier;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data) :
+    ID3v2::Frame(data)
+{
+  d = new UniqueFileIdentifierFramePrivate;
+  setData(data);
+}
+
+UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const String &owner, const ByteVector &id) :
+    ID3v2::Frame("UFID")
+{
+  d = new UniqueFileIdentifierFramePrivate;
+  d->owner = owner;
+  d->identifier = id;
+}
+
+UniqueFileIdentifierFrame::~UniqueFileIdentifierFrame()
+{
+  delete d;
+}
+
+String UniqueFileIdentifierFrame::owner() const
+{
+    return d->owner;
+}
+
+ByteVector UniqueFileIdentifierFrame::identifier() const
+{
+  return d->identifier;
+}
+
+void UniqueFileIdentifierFrame::setOwner(const String &s)
+{
+  d->owner = s;
+}
+
+void UniqueFileIdentifierFrame::setIdentifier(const ByteVector &v)
+{
+  d->identifier = v;
+}
+
+String UniqueFileIdentifierFrame::toString() const
+{
+  return String::null;
+}
+
+void UniqueFileIdentifierFrame::parseFields(const ByteVector &data)
+{
+  if(data.size() < 1) {
+    debug("An UFID frame must contain at least 1 byte.");
+    return;
+  }
+
+  int pos = 0;
+  d->owner = readStringField(data, String::Latin1, &pos);
+  d->identifier = data.mid(pos);
+}
+
+ByteVector UniqueFileIdentifierFrame::renderFields() const
+{
+  ByteVector data;
+
+  data.append(d->owner.data(String::Latin1));
+  data.append(char(0));
+  data.append(d->identifier);
+
+  return data;
+}
+
+UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data, Header *h) :
+  Frame(h)
+{
+  d = new UniqueFileIdentifierFramePrivate;
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h b/src/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h
new file mode 100644 (file)
index 0000000..b8a0039
--- /dev/null
@@ -0,0 +1,113 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_UNIQUEFILEIDENTIFIERFRAME
+#define TAGLIB_UNIQUEFILEIDENTIFIERFRAME
+
+#include "id3v2frame.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    /*!
+     * This is an implementation of ID3v2 unique file identifier frames.  This
+     * frame is used to identify the file in an arbitrary database identified
+     * by the owner field.
+     */
+
+    //! An implementation of ID3v2 unique identifier frames
+
+    class TAGLIB_EXPORT UniqueFileIdentifierFrame : public ID3v2::Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Creates a uniqe file identifier frame based on \a data.
+       */
+      UniqueFileIdentifierFrame(const ByteVector &data);
+
+      /*!
+       * Creates a unique file identifier frame with the owner \a owner and
+       * the identification \a id.
+       */
+      UniqueFileIdentifierFrame(const String &owner, const ByteVector &id);
+
+      /*!
+       * Destroys the frame.
+       */
+      ~UniqueFileIdentifierFrame();
+
+      /*!
+       * Returns the owner for the frame; essentially this is the key for
+       * determining which identification scheme this key belongs to.  This
+       * will usually either be an email address or URL for the person or tool
+       * used to create the unique identifier.
+       *
+       * \see setOwner()
+       */
+      String owner() const;
+
+      /*!
+       * Returns the unique identifier.  Though sometimes this is a text string
+       * it also may be binary data and as much should be assumed when handling
+       * it.
+       */
+      ByteVector identifier() const;
+
+      /*!
+       * Sets the owner of the identification scheme to \a s.
+       *
+       * \see owner()
+       */
+      void setOwner(const String &s);
+
+      /*!
+       * Sets the unique file identifier to \a v.
+       *
+       * \see identifier()
+       */
+      void setIdentifier(const ByteVector &v);
+
+      virtual String toString() const;
+
+    protected:
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame &);
+      UniqueFileIdentifierFrame &operator=(UniqueFileIdentifierFrame &);
+
+      UniqueFileIdentifierFrame(const ByteVector &data, Header *h);
+
+      class UniqueFileIdentifierFramePrivate;
+      UniqueFileIdentifierFramePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/unknownframe.cpp b/src/taglib/mpeg/id3v2/frames/unknownframe.cpp
new file mode 100644 (file)
index 0000000..42b87c6
--- /dev/null
@@ -0,0 +1,84 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "unknownframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class UnknownFrame::UnknownFramePrivate
+{
+public:
+  ByteVector fieldData;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data)
+{
+  d = new UnknownFramePrivate;
+  setData(data);
+}
+
+UnknownFrame::~UnknownFrame()
+{
+  delete d;
+}
+
+String UnknownFrame::toString() const
+{
+  return String::null;
+}
+
+ByteVector UnknownFrame::data() const
+{
+  return d->fieldData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void UnknownFrame::parseFields(const ByteVector &data)
+{
+  d->fieldData = data;
+}
+
+ByteVector UnknownFrame::renderFields() const
+{
+  return d->fieldData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new UnknownFramePrivate;
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/unknownframe.h b/src/taglib/mpeg/id3v2/frames/unknownframe.h
new file mode 100644 (file)
index 0000000..fd1dbbb
--- /dev/null
@@ -0,0 +1,79 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_UNKNOWNFRAME_H
+#define TAGLIB_UNKNOWNFRAME_H
+
+#include "id3v2frame.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! A frame type \e unknown to TagLib.
+
+    /*!
+     * This class represents a frame type not known (or more often simply
+     * unimplemented) in TagLib.  This is here provide a basic API for
+     * manipulating the binary data of unknown frames and to provide a means
+     * of rendering such \e unknown frames.
+     *
+     * Please note that a cleaner way of handling frame types that TagLib
+     * does not understand is to subclass ID3v2::Frame and ID3v2::FrameFactory
+     * to have your frame type supported through the standard ID3v2 mechanism.
+     */
+
+    class TAGLIB_EXPORT UnknownFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      UnknownFrame(const ByteVector &data);
+      virtual ~UnknownFrame();
+
+      virtual String toString() const;
+
+      /*!
+       * Returns the field data (everything but the header) for this frame.
+       */
+      ByteVector data() const;
+
+    protected:
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      UnknownFrame(const ByteVector &data, Header *h);
+      UnknownFrame(const UnknownFrame &);
+      UnknownFrame &operator=(const UnknownFrame &);
+
+      class UnknownFramePrivate;
+      UnknownFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
new file mode 100644 (file)
index 0000000..4c907dd
--- /dev/null
@@ -0,0 +1,162 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+    copyright            : (C) 2006 by Urs Fleisch
+    email                : ufleisch@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it  under the terms of the GNU Lesser General Public License version  *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "unsynchronizedlyricsframe.h"
+#include <tbytevectorlist.h>
+#include <tdebug.h>
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class UnsynchronizedLyricsFrame::UnsynchronizedLyricsFramePrivate
+{
+public:
+  UnsynchronizedLyricsFramePrivate() : textEncoding(String::Latin1) {}
+  String::Type textEncoding;
+  ByteVector language;
+  String description;
+  String text;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) :
+  Frame("USLT")
+{
+  d = new UnsynchronizedLyricsFramePrivate;
+  d->textEncoding = encoding;
+}
+
+UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) :
+  Frame(data)
+{
+  d = new UnsynchronizedLyricsFramePrivate;
+  setData(data);
+}
+
+UnsynchronizedLyricsFrame::~UnsynchronizedLyricsFrame()
+{
+  delete d;
+}
+
+String UnsynchronizedLyricsFrame::toString() const
+{
+  return d->text;
+}
+
+ByteVector UnsynchronizedLyricsFrame::language() const
+{
+  return d->language;
+}
+
+String UnsynchronizedLyricsFrame::description() const
+{
+  return d->description;
+}
+
+String UnsynchronizedLyricsFrame::text() const
+{
+  return d->text;
+}
+
+void UnsynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding)
+{
+  d->language = languageEncoding.mid(0, 3);
+}
+
+void UnsynchronizedLyricsFrame::setDescription(const String &s)
+{
+  d->description = s;
+}
+
+void UnsynchronizedLyricsFrame::setText(const String &s)
+{
+  d->text = s;
+}
+
+
+String::Type UnsynchronizedLyricsFrame::textEncoding() const
+{
+  return d->textEncoding;
+}
+
+void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
+{
+  d->textEncoding = encoding;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data)
+{
+  if(data.size() < 5) {
+    debug("An unsynchronized lyrics frame must contain at least 5 bytes.");
+    return;
+  }
+
+  d->textEncoding = String::Type(data[0]);
+  d->language = data.mid(1, 3);
+
+  int byteAlign
+    = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+
+  ByteVectorList l =
+    ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+  if(l.size() == 2) {
+    d->description = String(l.front(), d->textEncoding);
+    d->text = String(l.back(), d->textEncoding);
+  }
+}
+
+ByteVector UnsynchronizedLyricsFrame::renderFields() const
+{
+  ByteVector v;
+
+  v.append(char(d->textEncoding));
+  v.append(d->language.size() == 3 ? d->language : "XXX");
+  v.append(d->description.data(d->textEncoding));
+  v.append(textDelimiter(d->textEncoding));
+  v.append(d->text.data(d->textEncoding));
+
+  return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h)
+  : Frame(h)
+{
+  d = new UnsynchronizedLyricsFramePrivate();
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h b/src/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h
new file mode 100644 (file)
index 0000000..94c09d1
--- /dev/null
@@ -0,0 +1,157 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+    copyright            : (C) 2006 by Urs Fleisch
+    email                : ufleisch@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it  under the terms of the GNU Lesser General Public License version  *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H
+#define TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H
+
+#include "id3v2frame.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! ID3v2 unsynchronized lyrics frame
+    /*!
+     * An implementation of ID3v2 unsynchronized lyrics.
+     */
+    class TAGLIB_EXPORT UnsynchronizedLyricsFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Construct an empty unsynchronized lyrics frame that will use the text encoding
+       * \a encoding.
+       */
+      explicit UnsynchronizedLyricsFrame(String::Type encoding = String::Latin1);
+
+      /*!
+       * Construct a unsynchronized lyrics frame based on the data in \a data.
+       */
+      explicit UnsynchronizedLyricsFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this UnsynchronizedLyricsFrame instance.
+       */
+      virtual ~UnsynchronizedLyricsFrame();
+
+      /*!
+       * Returns the text of this unsynchronized lyrics frame.
+       *
+       * \see text()
+       */
+      virtual String toString() const;
+
+      /*!
+       * Returns the language encoding as a 3 byte encoding as specified by
+       * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>.
+       *
+       * \note Most taggers simply ignore this value.
+       *
+       * \see setLanguage()
+       */
+      ByteVector language() const;
+
+      /*!
+       * Returns the description of this unsynchronized lyrics frame.
+       *
+       * \note Most taggers simply ignore this value.
+       *
+       * \see setDescription()
+       */
+      String description() const;
+
+      /*!
+       * Returns the text of this unsynchronized lyrics frame.
+       *
+       * \see setText()
+       */
+      String text() const;
+
+      /*!
+       * Set the language using the 3 byte language code from
+       * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to
+       * \a languageCode.
+       *
+       * \see language()
+       */
+      void setLanguage(const ByteVector &languageCode);
+
+      /*!
+       * Sets the description of the unsynchronized lyrics frame to \a s.
+       *
+       * \see decription()
+       */
+      void setDescription(const String &s);
+
+      /*!
+       * Sets the text portion of the unsynchronized lyrics frame to \a s.
+       *
+       * \see text()
+       */
+      virtual void setText(const String &s);
+
+      /*!
+       * Returns the text encoding that will be used in rendering this frame.
+       * This defaults to the type that was either specified in the constructor
+       * or read from the frame when parsed.
+       *
+       * \see setTextEncoding()
+       * \see render()
+       */
+      String::Type textEncoding() const;
+
+      /*!
+       * Sets the text encoding to be used when rendering this frame to
+       * \a encoding.
+       *
+       * \see textEncoding()
+       * \see render()
+       */
+      void setTextEncoding(String::Type encoding);
+
+    protected:
+      // Reimplementations.
+
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      UnsynchronizedLyricsFrame(const ByteVector &data, Header *h);
+      UnsynchronizedLyricsFrame(const UnsynchronizedLyricsFrame &);
+      UnsynchronizedLyricsFrame &operator=(const UnsynchronizedLyricsFrame &);
+
+      class UnsynchronizedLyricsFramePrivate;
+      UnsynchronizedLyricsFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/src/taglib/mpeg/id3v2/frames/urllinkframe.cpp
new file mode 100644 (file)
index 0000000..25bdd18
--- /dev/null
@@ -0,0 +1,192 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+    copyright            : (C) 2006 by Urs Fleisch
+    email                : ufleisch@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "urllinkframe.h"
+#include <tdebug.h>
+#include <tstringlist.h>
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class UrlLinkFrame::UrlLinkFramePrivate
+{
+public:
+  String url;
+};
+
+class UserUrlLinkFrame::UserUrlLinkFramePrivate
+{
+public:
+  UserUrlLinkFramePrivate() : textEncoding(String::Latin1) {}
+  String::Type textEncoding;
+  String description;
+};
+
+UrlLinkFrame::UrlLinkFrame(const ByteVector &data) :
+  Frame(data)
+{
+  d = new UrlLinkFramePrivate;
+  setData(data);
+}
+
+UrlLinkFrame::~UrlLinkFrame()
+{
+  delete d;
+}
+
+void UrlLinkFrame::setUrl(const String &s)
+{
+  d->url = s;
+}
+
+String UrlLinkFrame::url() const
+{
+  return d->url;
+}
+
+void UrlLinkFrame::setText(const String &s)
+{
+  setUrl(s);
+}
+
+String UrlLinkFrame::toString() const
+{
+  return url();
+}
+
+void UrlLinkFrame::parseFields(const ByteVector &data)
+{
+  d->url = String(data);
+}
+
+ByteVector UrlLinkFrame::renderFields() const
+{
+  return d->url.data(String::Latin1);
+}
+
+UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+  d = new UrlLinkFramePrivate;
+  parseFields(fieldData(data));
+}
+
+
+UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) :
+  UrlLinkFrame("WXXX")
+{
+  d = new UserUrlLinkFramePrivate;
+  d->textEncoding = encoding;
+}
+
+UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) :
+  UrlLinkFrame(data)
+{
+  d = new UserUrlLinkFramePrivate;
+  setData(data);
+}
+
+UserUrlLinkFrame::~UserUrlLinkFrame()
+{
+  delete d;
+}
+
+String UserUrlLinkFrame::toString() const
+{
+  return "[" + description() + "] " + url();
+}
+
+String::Type UserUrlLinkFrame::textEncoding() const
+{
+  return d->textEncoding;
+}
+
+void UserUrlLinkFrame::setTextEncoding(String::Type encoding)
+{
+  d->textEncoding = encoding;
+}
+
+String UserUrlLinkFrame::description() const
+{
+  return d->description;
+}
+
+void UserUrlLinkFrame::setDescription(const String &s)
+{
+  d->description = s;
+}
+
+void UserUrlLinkFrame::parseFields(const ByteVector &data)
+{
+  if(data.size() < 2) {
+    debug("A user URL link frame must contain at least 2 bytes.");
+    return;
+  }
+
+  int pos = 0;
+
+  d->textEncoding = String::Type(data[0]);
+  pos += 1;
+
+  if(d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) {
+    int offset = data.find(textDelimiter(d->textEncoding), pos);
+    if(offset < pos)
+      return;
+
+    d->description = String(data.mid(pos, offset - pos), d->textEncoding);
+    pos = offset + 1;
+  }
+  else {
+    int len = data.mid(pos).find(textDelimiter(d->textEncoding), 0, 2);
+    if(len < 0)
+      return;
+
+    d->description = String(data.mid(pos, len), d->textEncoding);
+    pos += len + 2;
+  }
+
+  setUrl(String(data.mid(pos)));
+}
+
+ByteVector UserUrlLinkFrame::renderFields() const
+{
+  ByteVector v;
+
+  String::Type encoding = checkEncoding(d->description, d->textEncoding);
+
+  v.append(char(encoding));
+  v.append(d->description.data(encoding));
+  v.append(textDelimiter(encoding));
+  v.append(url().data(String::Latin1));
+
+  return v;
+}
+
+UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : UrlLinkFrame(data, h)
+{
+  d = new UserUrlLinkFramePrivate;
+  parseFields(fieldData(data));
+}
diff --git a/src/taglib/mpeg/id3v2/frames/urllinkframe.h b/src/taglib/mpeg/id3v2/frames/urllinkframe.h
new file mode 100644 (file)
index 0000000..4810de9
--- /dev/null
@@ -0,0 +1,172 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+    copyright            : (C) 2006 by Urs Fleisch
+    email                : ufleisch@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_URLLINKFRAME_H
+#define TAGLIB_URLLINKFRAME_H
+
+#include "id3v2frame.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! ID3v2 URL frame
+    /*!
+     * An implementation of ID3v2 URL link frames.
+     */
+    class TAGLIB_EXPORT UrlLinkFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * This is a dual purpose constructor.  \a data can either be binary data
+       * that should be parsed or (at a minimum) the frame ID.
+       */
+      explicit UrlLinkFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this UrlLinkFrame instance.
+       */
+      virtual ~UrlLinkFrame();
+
+      /*!
+       * Returns the URL.
+       */
+      virtual String url() const;
+
+      /*!
+       * Sets the URL to \a s.
+       */
+      virtual void setUrl(const String &s);
+
+      // Reimplementations.
+
+      virtual void setText(const String &s);
+      virtual String toString() const;
+
+    protected:
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      UrlLinkFrame(const ByteVector &data, Header *h);
+
+    private:
+      UrlLinkFrame(const UrlLinkFrame &);
+      UrlLinkFrame &operator=(const UrlLinkFrame &);
+
+      class UrlLinkFramePrivate;
+      UrlLinkFramePrivate *d;
+    };
+
+    //! ID3v2 User defined URL frame
+
+    /*!
+     * This is a specialization of URL link frames that allows for
+     * user defined entries.  Each entry has a description in addition to the
+     * normal list of fields that a URL link frame has.
+     *
+     * This description identifies the frame and must be unique.
+     */
+    class TAGLIB_EXPORT UserUrlLinkFrame : public UrlLinkFrame
+    {
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Constructs an empty user defined URL link frame.  For this to be
+       * a useful frame both a description and text must be set.
+       */
+      explicit UserUrlLinkFrame(String::Type encoding = String::Latin1);
+
+      /*!
+       * This is a dual purpose constructor.  \a data can either be binary data
+       * that should be parsed or (at a minimum) the frame ID.
+       */
+      explicit UserUrlLinkFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this UserUrlLinkFrame instance.
+       */
+      virtual ~UserUrlLinkFrame();
+
+      // Reimplementations.
+
+      virtual String toString() const;
+
+      /*!
+       * Returns the text encoding that will be used in rendering this frame.
+       * This defaults to the type that was either specified in the constructor
+       * or read from the frame when parsed.
+       *
+       * \see setTextEncoding()
+       * \see render()
+       */
+      String::Type textEncoding() const;
+
+      /*!
+       * Sets the text encoding to be used when rendering this frame to
+       * \a encoding.
+       *
+       * \see textEncoding()
+       * \see render()
+       */
+      void setTextEncoding(String::Type encoding);
+
+      /*!
+       * Returns the description for this frame.
+       */
+      String description() const;
+
+      /*!
+       * Sets the description of the frame to \a s.  \a s must be unique.
+       */
+      void setDescription(const String &s);
+
+    protected:
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      UserUrlLinkFrame(const ByteVector &data, Header *h);
+
+    private:
+      UserUrlLinkFrame(const UserUrlLinkFrame &);
+      UserUrlLinkFrame &operator=(const UserUrlLinkFrame &);
+
+      class UserUrlLinkFramePrivate;
+      UserUrlLinkFramePrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/id3v2.2.0.txt b/src/taglib/mpeg/id3v2/id3v2.2.0.txt
new file mode 100644 (file)
index 0000000..a69bddd
--- /dev/null
@@ -0,0 +1,1660 @@
+
+Informal standard                                             M. Nilsson
+Document: id3v2-00.txt                                   26th March 1998
+
+
+                            ID3 tag version 2
+
+Status of this document
+
+   This document is an Informal standard and is released so that
+   implementors could have a set standard before the formal standard is
+   set. The formal standard will use another version number if not
+   identical to what is described in this document. The contents in this
+   document may change for clarifications but never for added or altered
+   functionallity.
+
+   Distribution of this document is unlimited.
+
+
+Abstract
+
+   The recent gain of popularity for MPEG layer III audio files on the
+   internet forced a standardised way of storing information about an
+   audio file within itself to determinate its origin and contents.
+
+   Today the most accepted way to do this is with the so called ID3 tag,
+   which is simple but very limited and in some cases very unsuitable.
+   The ID3 tag has very limited space in every field, very limited
+   numbers of fields, not expandable or upgradeable and is placed at the
+   end of a the file, which is unsuitable for streaming audio. This draft
+   is an attempt to answer these issues with a new version of the ID3
+   tag.
+
+
+1.   Table of contents
+
+   2.   Conventions in this document
+   3.   ID3v2 overview
+     3.1.   ID3v2 header
+     3.2.   ID3v2 frames overview
+   4.   Declared ID3v2 frames
+     4.1.   Unique file identifier
+     4.2.   Text information frames
+       4.2.1.   Text information frames - details
+       4.2.2.   User defined text information frame
+     4.3.   URL link frames
+       4.3.1.   URL link frames - details
+       4.3.2.   User defined URL link frame
+     4.4.   Involved people list
+     4.5.   Music CD Identifier
+     4.6.   Event timing codes
+     4.7.   MPEG location lookup table
+     4.8.   Synced tempo codes
+     4.9.   Unsychronised lyrics/text transcription
+     4.10.  Synchronised lyrics/text
+     4.11.  Comments
+     4.12.  Relative volume adjustment
+     4.13.  Equalisation
+     4.14.  Reverb
+     4.15.  Attached picture
+     4.16.  General encapsulated object
+     4.17.  Play counter
+     4.18.  Popularimeter
+     4.19.  Recommended buffer size
+     4.20.  Encrypted meta frame
+     4.21.  Audio encryption
+     4.22.  Linked information
+   5.   The 'unsynchronisation scheme'
+   6.   Copyright
+   7.   References
+   8.   Appendix
+     A.   Appendix A - ID3-Tag Specification V1.1
+       A.1.   Overview
+       A.2.   ID3v1 Implementation
+       A.3.   Genre List
+       A.4.   Track addition - ID3v1.1
+   9.   Author's Address
+
+
+2.   Conventions in this document
+
+   In the examples, text within "" is a text string exactly as it appears
+   in a file. Numbers preceded with $ are hexadecimal and numbers
+   preceded with % are binary. $xx is used to indicate a byte with
+   unknown content. %x is used to indicate a bit with unknown content.
+   The most significant bit (MSB) of a byte is called 'bit 7' and the
+   least significant bit (LSB) is called 'bit 0'.
+
+   A tag is the whole tag described in this document. A frame is a block
+   of information in the tag. The tag consists of a header, frames and
+   optional padding. A field is a piece of information; one value, a
+   string etc. A numeric string is a string that consists of the
+   characters 0-9 only.
+
+
+3.   ID3v2 overview
+
+   The two biggest design goals were to be able to implement ID3v2
+   without disturbing old software too much and that ID3v2 should be
+   expandable.
+
+   The first criterion is met by the simple fact that the MPEG [MPEG]
+   decoding software uses a syncsignal, embedded in the audiostream, to
+   'lock on to' the audio. Since the ID3v2 tag doesn't contain a valid
+   syncsignal, no software will attempt to play the tag. If, for any
+   reason, coincidence make a syncsignal appear within the tag it will be
+   taken care of by the 'unsynchronisation scheme' described in section
+   5.
+
+   The second criterion has made a more noticeable impact on the design
+   of the ID3v2 tag. It is constructed as a container for several
+   information blocks, called frames, whose format need not be known to
+   the software that encounters them. At the start of every frame there
+   is an identifier that explains the frames's format and content, and a
+   size descriptor that allows software to skip unknown frames.
+
+   If a total revision of the ID3v2 tag should be needed, there is a
+   version number and a size descriptor in the ID3v2 header.
+
+   The ID3 tag described in this document is mainly targeted to files
+   encoded with MPEG-2 layer I, MPEG-2 layer II, MPEG-2 layer III and
+   MPEG-2.5, but may work with other types of encoded audio.
+
+   The bitorder in ID3v2 is most significant bit first (MSB). The
+   byteorder in multibyte numbers is most significant byte first (e.g.
+   $12345678 would be encoded $12 34 56 78).
+
+   It is permitted to include padding after all the final frame (at the
+   end of the ID3 tag), making the size of all the frames together
+   smaller than the size given in the head of the tag. A possible purpose
+   of this padding is to allow for adding a few additional frames or
+   enlarge existing frames within the tag without having to rewrite the
+   entire file. The value of the padding bytes must be $00.
+
+
+3.1.   ID3v2 header
+
+   The ID3v2 tag header, which should be the first information in the
+   file, is 10 bytes as follows:
+
+     ID3/file identifier      "ID3"
+     ID3 version              $02 00
+     ID3 flags                %xx000000
+     ID3 size             4 * %0xxxxxxx
+
+   The first three bytes of the tag are always "ID3" to indicate that
+   this is an ID3 tag, directly followed by the two version bytes. The
+   first byte of ID3 version is it's major version, while the second byte
+   is its revision number. All revisions are backwards compatible while
+   major versions are not. If software with ID3v2 and below support
+   should encounter version three or higher it should simply ignore the
+   whole tag. Version and revision will never be $FF.
+
+   The first bit (bit 7) in the 'ID3 flags' is indicating whether or not
+   unsynchronisation is used (see section 5 for details); a set bit
+   indicates usage.
+
+   The second bit (bit 6) is indicating whether or not compression is
+   used; a set bit indicates usage. Since no compression scheme has been
+   decided yet, the ID3 decoder (for now) should just ignore the entire
+   tag if the compression bit is set.
+
+   The ID3 tag size is encoded with four bytes where the first bit (bit
+   7) is set to zero in every byte, making a total of 28 bits. The zeroed
+   bits are ignored, so a 257 bytes long tag is represented as $00 00 02
+   01.
+
+   The ID3 tag size is the size of the complete tag after
+   unsychronisation, including padding, excluding the header (total tag
+   size - 10). The reason to use 28 bits (representing up to 256MB) for
+   size description is that we don't want to run out of space here.
+
+   A ID3v2 tag can be detected with the following pattern:
+     $49 44 33 yy yy xx zz zz zz zz
+   Where yy is less than $FF, xx is the 'flags' byte and zz is less than
+   $80.
+
+
+3.2.   ID3v2 frames overview
+
+   The headers of the frames are similar in their construction. They
+   consist of one three character identifier (capital A-Z and 0-9) and
+   one three byte size field, making a total of six bytes. The header is
+   excluded from the size. Identifiers beginning with "X", "Y" and "Z"
+   are for experimental use and free for everyone to use. Have in mind
+   that someone else might have used the same identifier as you. All
+   other identifiers are either used or reserved for future use.
+
+   The three character frame identifier is followed by a three byte size
+   descriptor, making a total header size of six bytes in every frame.
+   The size is calculated as framesize excluding frame identifier and
+   size descriptor (frame size - 6).
+
+   There is no fixed order of the frames' appearance in the tag, although
+   it is desired that the frames are arranged in order of significance
+   concerning the recognition of the file. An example of such order:
+   UFI, MCI, TT2 ...
+
+   A tag must contain at least one frame. A frame must be at least 1 byte
+   big, excluding the 6-byte header.
+
+   If nothing else is said a string is represented as ISO-8859-1
+   [ISO-8859-1] characters in the range $20 - $FF. All unicode strings
+   [UNICODE] use 16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). All
+   numeric strings are always encoded as ISO-8859-1. Terminated strings
+   are terminated with $00 if encoded with ISO-8859-1 and $00 00 if
+   encoded as unicode. If nothing else is said newline character is
+   forbidden. In ISO-8859-1 a new line is represented, when allowed, with
+   $0A only. Frames that allow different types of text encoding have a
+   text encoding description byte directly after the frame size. If
+   ISO-8859-1 is used this byte should be $00, if unicode is used it
+   should be $01.
+
+   The three byte language field is used to describe the language of the
+   frame's content, according to ISO-639-2 [ISO-639-2].
+
+   All URLs [URL] may be relative, e.g. "picture.png", "../doc.txt".
+
+   If a frame is longer than it should be, e.g. having more fields than
+   specified in this document, that indicates that additions to the
+   frame have been made in a later version of the ID3 standard. This
+   is reflected by the revision number in the header of the tag.
+
+
+4.   Declared ID3v2 frames
+
+   The following frames are declared in this draft.
+
+   4.19  BUF Recommended buffer size
+
+   4.17  CNT Play counter
+   4.11  COM Comments
+   4.21  CRA Audio encryption
+   4.20  CRM Encrypted meta frame
+
+   4.6   ETC Event timing codes
+   4.13  EQU Equalization
+
+   4.16  GEO General encapsulated object
+
+   4.4   IPL Involved people list
+
+   4.22  LNK Linked information
+
+   4.5   MCI Music CD Identifier
+   4.7   MLL MPEG location lookup table
+
+   4.15  PIC Attached picture
+   4.18  POP Popularimeter
+
+   4.14  REV Reverb
+   4.12  RVA Relative volume adjustment
+
+   4.10  SLT Synchronized lyric/text
+   4.8   STC Synced tempo codes
+
+   4.2.1 TAL Album/Movie/Show title
+   4.2.1 TBP BPM (Beats Per Minute)
+   4.2.1 TCM Composer
+   4.2.1 TCO Content type
+   4.2.1 TCR Copyright message
+   4.2.1 TDA Date
+   4.2.1 TDY Playlist delay
+   4.2.1 TEN Encoded by
+   4.2.1 TFT File type
+   4.2.1 TIM Time
+   4.2.1 TKE Initial key
+   4.2.1 TLA Language(s)
+   4.2.1 TLE Length
+   4.2.1 TMT Media type
+   4.2.1 TOA Original artist(s)/performer(s)
+   4.2.1 TOF Original filename
+   4.2.1 TOL Original Lyricist(s)/text writer(s)
+   4.2.1 TOR Original release year
+   4.2.1 TOT Original album/Movie/Show title
+   4.2.1 TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
+   4.2.1 TP2 Band/Orchestra/Accompaniment
+   4.2.1 TP3 Conductor/Performer refinement
+   4.2.1 TP4 Interpreted, remixed, or otherwise modified by
+   4.2.1 TPA Part of a set
+   4.2.1 TPB Publisher
+   4.2.1 TRC ISRC (International Standard Recording Code)
+   4.2.1 TRD Recording dates
+   4.2.1 TRK Track number/Position in set
+   4.2.1 TSI Size
+   4.2.1 TSS Software/hardware and settings used for encoding
+   4.2.1 TT1 Content group description
+   4.2.1 TT2 Title/Songname/Content description
+   4.2.1 TT3 Subtitle/Description refinement
+   4.2.1 TXT Lyricist/text writer
+   4.2.2 TXX User defined text information frame
+   4.2.1 TYE Year
+
+   4.1   UFI Unique file identifier
+   4.9   ULT Unsychronized lyric/text transcription
+
+   4.3.1 WAF Official audio file webpage
+   4.3.1 WAR Official artist/performer webpage
+   4.3.1 WAS Official audio source webpage
+   4.3.1 WCM Commercial information
+   4.3.1 WCP Copyright/Legal information
+   4.3.1 WPB Publishers official webpage
+   4.3.2 WXX User defined URL link frame
+
+
+4.1.   Unique file identifier
+
+   This frame's purpose is to be able to identify the audio file in a
+   database that may contain more information relevant to the content.
+   Since standardisation of such a database is beyond this document, all
+   frames begin with a null-terminated string with a URL [URL] containing
+   an email address, or a link to a location where an email address can
+   be found, that belongs to the organisation responsible for this
+   specific database implementation. Questions regarding the database
+   should be sent to the indicated email address. The URL should not be
+   used for the actual database queries. If a $00 is found directly after
+   the 'Frame size' the whole frame should be ignored, and preferably be
+   removed. The 'Owner identifier' is then followed by the actual
+   identifier, which may be up to 64 bytes. There may be more than one
+   "UFI" frame in a tag, but only one with the same 'Owner identifier'.
+
+     Unique file identifier  "UFI"
+     Frame size              $xx xx xx
+     Owner identifier        <textstring> $00
+     Identifier              <up to 64 bytes binary data>
+
+
+4.2.   Text information frames
+
+   The text information frames are the most important frames, containing
+   information like artist, album and more. There may only be one text
+   information frame of its kind in an tag. If the textstring is followed
+   by a termination ($00 (00)) all the following information should be
+   ignored and not be displayed. All the text information frames have the
+   following format:
+
+     Text information identifier  "T00" - "TZZ" , excluding "TXX",
+                                   described in 4.2.2.
+     Frame size                   $xx xx xx
+     Text encoding                $xx
+     Information                  <textstring>
+
+
+4.2.1.   Text information frames - details
+
+  TT1
+   The 'Content group description' frame is used if the sound belongs to
+   a larger category of sounds/music. For example, classical music is
+   often sorted in different musical sections (e.g. "Piano Concerto",
+   "Weather - Hurricane").
+
+  TT2
+   The 'Title/Songname/Content description' frame is the actual name of
+   the piece (e.g. "Adagio", "Hurricane Donna").
+
+  TT3
+   The 'Subtitle/Description refinement' frame is used for information
+   directly related to the contents title (e.g. "Op. 16" or "Performed
+   live at wembley").
+
+  TP1
+   The 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group' is
+   used for the main artist(s). They are seperated with the "/"
+   character.
+
+  TP2
+   The 'Band/Orchestra/Accompaniment' frame is used for additional
+   information about the performers in the recording.
+
+  TP3
+   The 'Conductor' frame is used for the name of the conductor.
+   
+  TP4
+   The 'Interpreted, remixed, or otherwise modified by' frame contains
+   more information about the people behind a remix and similar
+   interpretations of another existing piece.
+
+  TCM
+   The 'Composer(s)' frame is intended for the name of the composer(s).
+   They are seperated with the "/" character.
+
+  TXT
+   The 'Lyricist(s)/text writer(s)' frame is intended for the writer(s)
+   of the text or lyrics in the recording. They are seperated with the
+   "/" character.
+
+  TLA
+   The 'Language(s)' frame should contain the languages of the text or
+   lyrics in the audio file. The language is represented with three
+   characters according to ISO-639-2. If more than one language is used
+   in the text their language codes should follow according to their
+   usage.
+
+  TCO
+   The content type, which previously (in ID3v1.1, see appendix A) was
+   stored as a one byte numeric value only, is now a numeric string. You
+   may use one or several of the types as ID3v1.1 did or, since the
+   category list would be impossible to maintain with accurate and up to
+   date categories, define your own.
+   References to the ID3v1 genres can be made by, as first byte, enter
+   "(" followed by a number from the genres list (section A.3.) and
+   ended with a ")" character. This is optionally followed by a
+   refinement, e.g. "(21)" or "(4)Eurodisco". Several references can be
+   made in the same frame, e.g. "(51)(39)". If the refinement should
+   begin with a "(" character it should be replaced with "((", e.g. "((I
+   can figure out any genre)" or "(55)((I think...)". The following new
+   content types is defined in ID3v2 and is implemented in the same way
+   as the numerig content types, e.g. "(RX)".
+   
+     RX  Remix
+     CR  Cover
+
+  TAL
+   The 'Album/Movie/Show title' frame is intended for the title of the
+   recording(/source of sound) which the audio in the file is taken from.
+   
+  TPA
+   The 'Part of a set' frame is a numeric string that describes which
+   part of a set the audio came from. This frame is used if the source
+   described in the "TAL" frame is divided into several mediums, e.g. a
+   double CD. The value may be extended with a "/" character and a
+   numeric string containing the total number of parts in the set. E.g.
+   "1/2".
+
+  TRK
+   The 'Track number/Position in set' frame is a numeric string
+   containing the order number of the audio-file on its original
+   recording. This may be extended with a "/" character and a numeric
+   string containing the total numer of tracks/elements on the original
+   recording. E.g. "4/9".
+
+  TRC
+   The 'ISRC' frame should contian the International Standard Recording
+   Code [ISRC].
+
+  TYE
+   The 'Year' frame is a numeric string with a year of the recording.
+   This frames is always four characters long (until the year 10000).
+
+  TDA
+   The 'Date' frame is a numeric string in the DDMM format containing
+   the date for the recording. This field is always four characters
+   long.
+
+  TIM
+   The 'Time' frame is a numeric string in the HHMM format containing
+   the time for the recording. This field is always four characters
+   long.
+   
+  TRD
+   The 'Recording dates' frame is a intended to be used as complement to
+   the "TYE", "TDA" and "TIM" frames. E.g. "4th-7th June, 12th June" in
+   combination with the "TYE" frame.
+
+  TMT
+   The 'Media type' frame describes from which media the sound
+   originated. This may be a textstring or a reference to the predefined
+   media types found in the list below. References are made within "("
+   and ")" and are optionally followed by a text refinement, e.g. "(MC)
+   with four channels". If a text refinement should begin with a "("
+   character it should be replaced with "((" in the same way as in the
+   "TCO" frame. Predefined refinements is appended after the media type,
+   e.g. "(CD/S)" or "(VID/PAL/VHS)".
+
+    DIG    Other digital media
+      /A    Analog transfer from media
+
+    ANA    Other analog media
+      /WAC  Wax cylinder
+      /8CA  8-track tape cassette
+
+    CD     CD
+      /A    Analog transfer from media
+      /DD   DDD
+      /AD   ADD
+      /AA   AAD
+
+    LD     Laserdisc
+      /A     Analog transfer from media
+
+    TT     Turntable records
+      /33    33.33 rpm
+      /45    45 rpm
+      /71    71.29 rpm
+      /76    76.59 rpm
+      /78    78.26 rpm
+      /80    80 rpm
+     
+    MD     MiniDisc
+      /A    Analog transfer from media
+     
+    DAT    DAT
+      /A    Analog transfer from media
+      /1    standard, 48 kHz/16 bits, linear
+      /2    mode 2, 32 kHz/16 bits, linear
+      /3    mode 3, 32 kHz/12 bits, nonlinear, low speed
+      /4    mode 4, 32 kHz/12 bits, 4 channels
+      /5    mode 5, 44.1 kHz/16 bits, linear
+      /6    mode 6, 44.1 kHz/16 bits, 'wide track' play
+     
+    DCC    DCC
+      /A    Analog transfer from media
+    
+    DVD    DVD
+      /A    Analog transfer from media
+    
+    TV     Television
+      /PAL    PAL
+      /NTSC   NTSC
+      /SECAM  SECAM
+    
+    VID    Video
+      /PAL    PAL
+      /NTSC   NTSC
+      /SECAM  SECAM
+      /VHS    VHS
+      /SVHS   S-VHS
+      /BETA   BETAMAX
+    
+    RAD    Radio
+      /FM   FM
+      /AM   AM
+      /LW   LW
+      /MW   MW
+    
+    TEL    Telephone
+      /I    ISDN
+    
+    MC     MC (normal cassette)
+      /4    4.75 cm/s (normal speed for a two sided cassette)
+      /9    9.5 cm/s
+      /I    Type I cassette (ferric/normal)
+      /II   Type II cassette (chrome)
+      /III  Type III cassette (ferric chrome)
+      /IV   Type IV cassette (metal)
+    
+    REE    Reel
+      /9    9.5 cm/s
+      /19   19 cm/s
+      /38   38 cm/s
+      /76   76 cm/s
+      /I    Type I cassette (ferric/normal)
+      /II   Type II cassette (chrome)
+      /III  Type III cassette (ferric chrome)
+      /IV   Type IV cassette (metal)
+
+  TFT
+   The 'File type' frame indicates which type of audio this tag defines.
+   The following type and refinements are defined:
+   
+     MPG    MPEG Audio
+       /1     MPEG 2 layer I
+       /2     MPEG 2 layer II
+       /3     MPEG 2 layer III
+       /2.5   MPEG 2.5
+       /AAC   Advanced audio compression
+     
+   but other types may be used, not for these types though. This is used
+   in a similar way to the predefined types in the "TMT" frame, but
+   without parenthesis. If this frame is not present audio type is
+   assumed to be "MPG".
+
+  TBP
+   BPM is short for beats per minute, and is easily computed by
+   dividing the number of beats in a musical piece with its length. To
+   get a more accurate result, do the BPM calculation on the main-part
+   only. To acquire best result measure the time between each beat and
+   calculate individual BPM for each beat and use the median value as
+   result. BPM is an integer and represented as a numerical string.
+
+  TCR
+   The 'Copyright message' frame, which must begin with a year and a
+   space character (making five characters), is intended for the
+   copyright holder of the original sound, not the audio file itself. The
+   absence of this frame means only that the copyright information is
+   unavailable or has been removed, and must not be interpreted to mean
+   that the sound is public domain. Every time this field is displayed
+   the field must be preceded with "Copyright " (C) " ", where (C) is one
+   character showing a C in a circle.
+
+  TPB
+   The 'Publisher' frame simply contains the name of the label or
+   publisher.
+
+  TEN
+   The 'Encoded by' frame contains the name of the person or
+   organisation that encoded the audio file. This field may contain a
+   copyright message, if the audio file also is copyrighted by the
+   encoder.
+
+  TSS
+   The 'Software/hardware and settings used for encoding' frame
+   includes the used audio encoder and its settings when the file was
+   encoded. Hardware refers to hardware encoders, not the computer on
+   which a program was run.
+
+  TOF
+   The 'Original filename' frame contains the preferred filename for the
+   file, since some media doesn't allow the desired length of the
+   filename. The filename is case sensitive and includes its suffix.
+
+  TLE
+   The 'Length' frame contains the length of the audiofile in
+   milliseconds, represented as a numeric string.
+
+  TSI
+   The 'Size' frame contains the size of the audiofile in bytes
+   excluding the tag, represented as a numeric string.
+
+  TDY
+   The 'Playlist delay' defines the numbers of milliseconds of silence
+   between every song in a playlist. The player should use the "ETC"
+   frame, if present, to skip initial silence and silence at the end of
+   the audio to match the 'Playlist delay' time. The time is represented
+   as a numeric string.
+
+  TKE
+   The 'Initial key' frame contains the musical key in which the sound
+   starts. It is represented as a string with a maximum length of three
+   characters. The ground keys are represented with "A","B","C","D","E",
+   "F" and "G" and halfkeys represented with "b" and "#". Minor is
+   represented as "m". Example "Cbm". Off key is represented with an "o"
+   only.
+
+  TOT
+   The 'Original album/Movie/Show title' frame is intended for the title
+   of the original recording(/source of sound), if for example the music
+   in the file should be a cover of a previously released song.
+   
+  TOA
+   The 'Original artist(s)/performer(s)' frame is intended for the
+   performer(s) of the original recording, if for example the music in
+   the file should be a cover of a previously released song. The
+   performers are seperated with the "/" character.
+
+  TOL
+   The 'Original Lyricist(s)/text writer(s)' frame is intended for the
+   text writer(s) of the original recording, if for example the music in
+   the file should be a cover of a previously released song. The text
+   writers are seperated with the "/" character.
+
+  TOR
+   The 'Original release year' frame is intended for the year when the
+   original recording, if for example the music in the file should be a
+   cover of a previously released song, was released. The field is
+   formatted as in the "TDY" frame.
+
+
+4.2.2.   User defined text information frame
+
+   This frame is intended for one-string text information concerning the
+   audiofile in a similar way to the other "T"xx frames. The frame body
+   consists of a description of the string, represented as a terminated
+   string, followed by the actual string. There may be more than one
+   "TXX" frame in each tag, but only one with the same description.
+
+     User defined...   "TXX"
+     Frame size        $xx xx xx
+     Text encoding     $xx
+     Description       <textstring> $00 (00)
+     Value             <textstring>
+
+
+4.3.   URL link frames
+
+   With these frames dynamic data such as webpages with touring
+   information, price information or plain ordinary news can be added to
+   the tag. There may only be one URL [URL] link frame of its kind in an
+   tag, except when stated otherwise in the frame description. If the
+   textstring is followed by a termination ($00 (00)) all the following
+   information should be ignored and not be displayed. All URL link
+   frames have the following format:
+
+     URL link frame   "W00" - "WZZ" , excluding "WXX" 
+                                      (described in 4.3.2.)
+     Frame size       $xx xx xx
+     URL              <textstring>
+
+
+4.3.1.   URL link frames - details
+
+  WAF
+   The 'Official audio file webpage' frame is a URL pointing at a file
+   specific webpage.
+   
+  WAR
+   The 'Official artist/performer webpage' frame is a URL pointing at
+   the artists official webpage. There may be more than one "WAR" frame
+   in a tag if the audio contains more than one performer.
+   
+  WAS
+   The 'Official audio source webpage' frame is a URL pointing at the
+   official webpage for the source of the audio file, e.g. a movie.
+   
+  WCM
+   The 'Commercial information' frame is a URL pointing at a webpage
+   with information such as where the album can be bought. There may be
+   more than one "WCM" frame in a tag.
+   
+  WCP
+   The 'Copyright/Legal information' frame is a URL pointing at a
+   webpage where the terms of use and ownership of the file is described.
+   
+  WPB
+   The 'Publishers official webpage' frame is a URL pointing at the
+   official wepage for the publisher.
+
+
+4.3.2.   User defined URL link frame
+
+   This frame is intended for URL [URL] links concerning the audiofile in
+   a similar way to the other "W"xx frames. The frame body consists of a
+   description of the string, represented as a terminated string,
+   followed by the actual URL. The URL is always encoded with ISO-8859-1
+   [ISO-8859-1]. There may be more than one "WXX" frame in each tag, but
+   only one with the same description.
+
+     User defined...   "WXX"
+     Frame size        $xx xx xx
+     Text encoding     $xx
+     Description       <textstring> $00 (00)
+     URL               <textstring>
+
+
+4.4.   Involved people list
+
+   Since there might be a lot of people contributing to an audio file in
+   various ways, such as musicians and technicians, the 'Text
+   information frames' are often insufficient to list everyone involved
+   in a project. The 'Involved people list' is a frame containing the
+   names of those involved, and how they were involved. The body simply
+   contains a terminated string with the involvement directly followed by
+   a terminated string with the involvee followed by a new involvement
+   and so on. There may only be one "IPL" frame in each tag.
+
+     Involved people list   "IPL"
+     Frame size             $xx xx xx
+     Text encoding          $xx
+     People list strings    <textstrings>
+
+
+4.5.   Music CD Identifier
+
+   This frame is intended for music that comes from a CD, so that the CD
+   can be identified in databases such as the CDDB [CDDB]. The frame
+   consists of a binary dump of the Table Of Contents, TOC,  from the CD,
+   which is a header of 4 bytes and then 8 bytes/track on the CD making a
+   maximum of 804 bytes. This frame requires a present and valid "TRK"
+   frame. There may only be one "MCI" frame in each tag.
+
+     Music CD identifier   "MCI"
+     Frame size            $xx xx xx
+     CD TOC                <binary data>
+
+
+4.6.   Event timing codes
+
+   This frame allows synchronisation with key events in a song or sound.
+   The head is:
+
+     Event timing codes   "ETC"
+     Frame size           $xx xx xx
+     Time stamp format    $xx
+     
+   Where time stamp format is:
+     
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Abolute time means that every stamp contains the time from the
+   beginning of the file.
+
+   Followed by a list of key events in the following format:
+
+     Type of event   $xx
+     Time stamp      $xx (xx ...)
+
+   The 'Time stamp' is set to zero if directly at the beginning of the
+   sound or after the previous event. All events should be sorted in
+   chronological order. The type of event is as follows:
+
+     $00  padding (has no meaning)
+     $01  end of initial silence
+     $02  intro start
+     $03  mainpart start
+     $04  outro start
+     $05  outro end
+     $06  verse begins
+     $07  refrain begins
+     $08  interlude
+     $09  theme start
+     $0A  variation
+     $0B  key change
+     $0C  time change
+     $0D  unwanted noise (Snap, Crackle & Pop)
+
+     $0E-$DF  reserved for future use
+
+     $E0-$EF  not predefined sync 0-F
+
+     $F0-$FC  reserved for future use
+
+     $FD  audio end (start of silence)
+     $FE  audio file ends
+     $FF  one more byte of events follows (all the following bytes with
+          the value $FF have the same function)
+
+   The 'Not predefined sync's ($E0-EF) are for user events. You might
+   want to synchronise your music to something, like setting of an
+   explosion on-stage, turning on your screensaver etc.
+
+   There may only be one "ETC" frame in each tag.
+
+
+4.7.   MPEG location lookup table
+
+   To increase performance and accuracy of jumps within a MPEG [MPEG]
+   audio file, frames with timecodes in different locations in the file
+   might be useful. The ID3 frame includes references that the software
+   can use to calculate positions in the file. After the frame header is
+   a descriptor of how much the 'frame counter' should increase for every
+   reference. If this value is two then the first reference points out
+   the second frame, the 2nd reference the 4th frame, the 3rd reference
+   the 6th frame etc. In a similar way the 'bytes between reference' and
+   'milliseconds between reference' points out bytes and milliseconds
+   respectively.
+   
+   Each reference consists of two parts; a certain number of bits, as
+   defined in 'bits for bytes deviation', that describes the difference
+   between what is said in 'bytes between reference' and the reality and
+   a certain number of bits, as defined in 'bits for milliseconds
+   deviation', that describes the difference between what is said in
+   'milliseconds between reference' and the reality. The number of bits
+   in every reference, i.e. 'bits for bytes deviation'+'bits for
+   milliseconds deviation', must be a multiple of four. There may only be
+   one "MLL" frame in each tag.
+   
+     Location lookup table          "MLL"
+     ID3 frame size                 $xx xx xx
+     MPEG frames between reference  $xx xx
+     Bytes between reference        $xx xx xx
+     Milliseconds between reference $xx xx xx
+     Bits for bytes deviation       $xx
+     Bits for milliseconds dev.     $xx
+
+   Then for every reference the following data is included;
+
+     Deviation in bytes         %xxx....
+     Deviation in milliseconds  %xxx....
+
+
+4.8.   Synced tempo codes
+
+   For a more accurate description of the tempo of a musical piece this
+   frame might be used. After the header follows one byte describing
+   which time stamp format should be used. Then follows one or more tempo
+   codes. Each tempo code consists of one tempo part and one time part.
+   The tempo is in BPM described with one or two bytes. If the first byte
+   has the value $FF, one more byte follows, which is added to the first
+   giving a range from 2 - 510 BPM, since $00 and $01 is reserved. $00 is
+   used to describe a beat-free time period, which is not the same as a
+   music-free time period. $01 is used to indicate one single beat-stroke
+   followed by a beat-free period.
+
+   The tempo descriptor is followed by a time stamp. Every time the tempo
+   in the music changes, a tempo descriptor may indicate this for the
+   player. All tempo descriptors should be sorted in chronological order.
+   The first beat-stroke in a time-period is at the same time as the beat
+   description occurs. There may only be one "STC" frame in each tag.
+
+     Synced tempo codes  "STC"
+     Frame size          $xx xx xx
+     Time stamp format   $xx
+     Tempo data          <binary data>
+
+   Where time stamp format is:
+     
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Abolute time means that every stamp contains the time from the
+   beginning of the file.
+
+
+4.9.   Unsychronised lyrics/text transcription
+
+   This frame contains the lyrics of the song or a text transcription of
+   other vocal activities. The head includes an encoding descriptor and
+   a content descriptor. The body consists of the actual text. The
+   'Content descriptor' is a terminated string. If no descriptor is
+   entered, 'Content descriptor' is $00 (00) only. Newline characters
+   are allowed in the text. Maximum length for the descriptor is 64
+   bytes. There may be more than one lyrics/text frame in each tag, but
+   only one with the same language and content descriptor.
+
+     Unsynced lyrics/text "ULT"
+     Frame size           $xx xx xx
+     Text encoding        $xx
+     Language             $xx xx xx
+     Content descriptor   <textstring> $00 (00)
+     Lyrics/text          <textstring>
+
+
+4.10.   Synchronised lyrics/text
+
+   This is another way of incorporating the words, said or sung lyrics,
+   in the audio file as text, this time, however, in sync with the audio.
+   It might also be used to describing events e.g. occurring on a stage
+   or on the screen in sync with the audio. The header includes a content
+   descriptor, represented with as terminated textstring. If no
+   descriptor is entered, 'Content descriptor' is $00 (00) only.
+
+     Synced lyrics/text   "SLT"
+     Frame size           $xx xx xx
+     Text encoding        $xx
+     Language             $xx xx xx
+     Time stamp format    $xx
+     Content type         $xx
+     Content descriptor   <textstring> $00 (00)
+
+
+   Encoding:   $00  ISO-8859-1 [ISO-8859-1] character set is used => $00
+                    is sync identifier.
+               $01  Unicode [UNICODE] character set is used => $00 00 is
+                    sync identifier.
+
+   Content type:   $00 is other
+                   $01 is lyrics
+                   $02 is text transcription
+                   $03 is movement/part name (e.g. "Adagio")
+                   $04 is events (e.g. "Don Quijote enters the stage")
+                   $05 is chord (e.g. "Bb F Fsus")
+
+   Time stamp format is:
+     
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Abolute time means that every stamp contains the time from the
+   beginning of the file.
+
+   The text that follows the frame header differs from that of the
+   unsynchronised lyrics/text transcription in one major way. Each
+   syllable (or whatever size of text is considered to be convenient by
+   the encoder) is a null terminated string followed by a time stamp
+   denoting where in the sound file it belongs. Each sync thus has the
+   following structure:
+
+     Terminated text to be synced (typically a syllable)
+     Sync identifier (terminator to above string)   $00 (00)
+     Time stamp                                     $xx (xx ...)
+
+   The 'time stamp' is set to zero or the whole sync is omitted if
+   located directly at the beginning of the sound. All time stamps should
+   be sorted in chronological order. The sync can be considered as a
+   validator of the subsequent string.
+
+   Newline characters are allowed in all "SLT" frames and should be used
+   after every entry (name, event etc.) in a frame with the content type
+   $03 - $04.
+
+   A few considerations regarding whitespace characters: Whitespace
+   separating words should mark the beginning of a new word, thus
+   occurring in front of the first syllable of a new word. This is also
+   valid for new line characters. A syllable followed by a comma should
+   not be broken apart with a sync (both the syllable and the comma
+   should be before the sync).
+
+   An example: The "ULT" passage
+
+     "Strangers in the night" $0A "Exchanging glances"
+
+   would be "SLT" encoded as:
+
+     "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx
+     " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx
+     xx "glan" $00 xx xx "ces" $00 xx xx
+
+   There may be more than one "SLT" frame in each tag, but only one with
+   the same language and content descriptor.
+
+
+4.11.   Comments
+
+   This frame replaces the old 30-character comment field in ID3v1. It
+   consists of a frame head followed by encoding, language and content
+   descriptors and is ended with the actual comment as a text string.
+   Newline characters are allowed in the comment text string. There may
+   be more than one comment frame in each tag, but only one with the same
+   language and content descriptor.
+
+     Comment                   "COM"
+     Frame size                $xx xx xx
+     Text encoding             $xx
+     Language                  $xx xx xx
+     Short content description <textstring> $00 (00)
+     The actual text           <textstring>
+
+
+4.12.   Relative volume adjustment
+
+   This is a more subjective function than the previous ones. It allows
+   the user to say how much he wants to increase/decrease the volume on
+   each channel while the file is played. The purpose is to be able to
+   align all files to a reference volume, so that you don't have to
+   change the volume constantly. This frame may also be used to balance
+   adjust the audio. If the volume peak levels are known then this could
+   be described with the 'Peak volume right' and 'Peak volume left'
+   field. If Peakvolume is not known these fields could be left zeroed
+   or completely omitted.  There may only be one "RVA" frame in each
+   tag.
+
+     Relative volume adjustment    "RVA"
+     Frame size                    $xx xx xx
+     Increment/decrement           %000000xx
+     Bits used for volume descr.   $xx
+     Relative volume change, right $xx xx (xx ...)
+     Relative volume change, left  $xx xx (xx ...)
+     Peak volume right             $xx xx (xx ...)
+     Peak volume left              $xx xx (xx ...)
+
+   In the increment/decrement field bit 0 is used to indicate the right
+   channel and bit 1 is used to indicate the left channel. 1 is
+   increment and 0 is decrement.
+
+   The 'bits used for volume description' field is normally $10 (16 bits)
+   for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not
+   be $00. The volume is always represented with whole bytes, padded in
+   the beginning (highest bits) when 'bits used for volume description'
+   is not a multiple of eight.
+
+
+4.13.   Equalisation
+
+   This is another subjective, alignment frame. It allows the user to
+   predefine an equalisation curve within the audio file. There may only
+   be one "EQU" frame in each tag.
+
+     Equalisation       "EQU"
+     Frame size         $xx xx xx
+     Adjustment bits    $xx
+
+   The 'adjustment bits' field defines the number of bits used for
+   representation of the adjustment. This is normally $10 (16 bits) for
+   MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be
+   $00.
+
+   This is followed by 2 bytes + ('adjustment bits' rounded up to the
+   nearest byte) for every equalisation band in the following format,
+   giving a frequency range of 0 - 32767Hz:
+
+     Increment/decrement   %x (MSB of the Frequency)
+     Frequency             (lower 15 bits)
+     Adjustment            $xx (xx ...)
+
+   The increment/decrement bit is 1 for increment and 0 for decrement.
+   The equalisation bands should be ordered increasingly with reference
+   to frequency. All frequencies don't have to be declared. Adjustments
+   with the value $00 should be omitted. A frequency should only be
+   described once in the frame.
+
+
+4.14.   Reverb
+
+   Yet another subjective one. You may here adjust echoes of different
+   kinds. Reverb left/right is the delay between every bounce in ms.
+   Reverb bounces left/right is the number of bounces that should be
+   made. $FF equals an infinite number of bounces. Feedback is the amount
+   of volume that should be returned to the next echo bounce. $00 is 0%,
+   $FF is 100%. If this value were $7F, there would be 50% volume
+   reduction on the first bounce, yet 50% on the second and so on. Left
+   to left means the sound from the left bounce to be played in the left
+   speaker, while left to right means sound from the left bounce to be
+   played in the right speaker.
+
+   'Premix left to right' is the amount of left sound to be mixed in the
+   right before any reverb is applied, where $00 id 0% and $FF is 100%.
+   'Premix right to left' does the same thing, but right to left. Setting
+   both premix to $FF would result in a mono output (if the reverb is
+   applied symmetric). There may only be one "REV" frame in each tag.
+
+     Reverb settings                  "REV"
+     Frame size                       $00 00 0C
+     Reverb left (ms)                 $xx xx
+     Reverb right (ms)                $xx xx
+     Reverb bounces, left             $xx
+     Reverb bounces, right            $xx
+     Reverb feedback, left to left    $xx
+     Reverb feedback, left to right   $xx
+     Reverb feedback, right to right  $xx
+     Reverb feedback, right to left   $xx
+     Premix left to right             $xx
+     Premix right to left             $xx
+
+
+4.15.   Attached picture
+
+   This frame contains a picture directly related to the audio file.
+   Image format is preferably "PNG" [PNG] or "JPG" [JFIF]. Description
+   is a short description of the picture, represented as a terminated
+   textstring. The description has a maximum length of 64 characters,
+   but may be empty. There may be several pictures attached to one file,
+   each in their individual "PIC" frame, but only one with the same
+   content descriptor. There may only be one picture with the picture
+   type declared as picture type $01 and $02 respectively. There is a
+   possibility to put only a link to the image file by using the 'image
+   format' "-->" and having a complete URL [URL] instead of picture data.
+   The use of linked files should however be used restrictively since
+   there is the risk of separation of files.
+
+     Attached picture   "PIC"
+     Frame size         $xx xx xx
+     Text encoding      $xx
+     Image format       $xx xx xx
+     Picture type       $xx
+     Description        <textstring> $00 (00)
+     Picture data       <binary data>
+
+
+   Picture type:  $00  Other
+                  $01  32x32 pixels 'file icon' (PNG only)
+                  $02  Other file icon
+                  $03  Cover (front)
+                  $04  Cover (back)
+                  $05  Leaflet page
+                  $06  Media (e.g. lable side of CD)
+                  $07  Lead artist/lead performer/soloist
+                  $08  Artist/performer
+                  $09  Conductor
+                  $0A  Band/Orchestra
+                  $0B  Composer
+                  $0C  Lyricist/text writer
+                  $0D  Recording Location
+                  $0E  During recording
+                  $0F  During performance
+                  $10  Movie/video screen capture
+                  $11  A bright coloured fish
+                  $12  Illustration
+                  $13  Band/artist logotype
+                  $14  Publisher/Studio logotype
+
+
+4.16.   General encapsulated object
+
+   In this frame any type of file can be encapsulated. After the header,
+   'Frame size' and 'Encoding' follows 'MIME type' [MIME] and 'Filename'
+   for the encapsulated object, both represented as terminated strings
+   encoded with ISO 8859-1 [ISO-8859-1]. The filename is case sensitive.
+   Then follows a content description as terminated string, encoded as
+   'Encoding'. The last thing in the frame is the actual object. The
+   first two strings may be omitted, leaving only their terminations.
+   MIME type is always an ISO-8859-1 text string. There may be more than
+   one "GEO" frame in each tag, but only one with the same content
+   descriptor.
+
+     General encapsulated object "GEO"
+     Frame size                  $xx xx xx
+     Text encoding               $xx
+     MIME type                   <textstring> $00
+     Filename                    <textstring> $00 (00)
+     Content description         <textstring> $00 (00)
+     Encapsulated object         <binary data>
+
+
+4.17.   Play counter
+
+   This is simply a counter of the number of times a file has been
+   played. The value is increased by one every time the file begins to
+   play. There may only be one "CNT" frame in each tag. When the counter
+   reaches all one's, one byte is inserted in front of the counter thus
+   making the counter eight bits bigger.  The counter must be at least
+   32-bits long to begin with.
+
+     Play counter   "CNT"
+     Frame size     $xx xx xx
+     Counter        $xx xx xx xx (xx ...)
+
+
+4.18.   Popularimeter
+
+   The purpose of this frame is to specify how good an audio file is.
+   Many interesting applications could be found to this frame such as a
+   playlist that features better audiofiles more often than others or it
+   could be used to profile a persons taste and find other 'good' files
+   by comparing people's profiles. The frame is very simple. It contains
+   the email address to the user, one rating byte and a four byte play
+   counter, intended to be increased with one for every time the file is
+   played. The email is a terminated string. The rating is 1-255 where
+   1 is worst and 255 is best. 0 is unknown. If no personal counter is
+   wanted it may be omitted.  When the counter reaches all one's, one
+   byte is inserted in front of the counter thus making the counter
+   eight bits bigger in the same away as the play counter ("CNT").
+   There may be more than one "POP" frame in each tag, but only one with
+   the same email address.
+   
+     Popularimeter   "POP"
+     Frame size      $xx xx xx
+     Email to user   <textstring> $00
+     Rating          $xx
+     Counter         $xx xx xx xx (xx ...)
+
+
+4.19.   Recommended buffer size
+
+   Sometimes the server from which a audio file is streamed is aware of
+   transmission or coding problems resulting in interruptions in the
+   audio stream. In these cases, the size of the buffer can be
+   recommended by the server using this frame. If the 'embedded info
+   flag' is true (1) then this indicates that an ID3 tag with the
+   maximum size described in 'Buffer size' may occur in the audiostream.
+   In such case the tag should reside between two MPEG [MPEG] frames, if
+   the audio is MPEG encoded. If the position of the next tag is known,
+   'offset to next tag' may be used. The offset is calculated from the
+   end of tag in which this frame resides to the first byte of the header
+   in the next. This field may be omitted. Embedded tags is currently not
+   recommended since this could render unpredictable behaviour from
+   present software/hardware. The 'Buffer size' should be kept to a
+   minimum. There may only be one "BUF" frame in each tag.
+
+     Recommended buffer size   "BUF"
+     Frame size                $xx xx xx
+     Buffer size               $xx xx xx
+     Embedded info flag        %0000000x
+     Offset to next tag        $xx xx xx xx
+
+
+4.20.   Encrypted meta frame
+
+   This frame contains one or more encrypted frames. This enables
+   protection of copyrighted information such as pictures and text, that
+   people might want to pay extra for. Since standardisation of such an
+   encryption scheme is beyond this document, all "CRM" frames begin with
+   a terminated string with a URL [URL] containing an email address, or a
+   link to a location where an email adress can be found, that belongs to
+   the organisation responsible for this specific encrypted meta frame.
+
+   Questions regarding the encrypted frame should be sent to the
+   indicated email address. If a $00 is found directly after the 'Frame
+   size', the whole frame should be ignored, and preferably be removed.
+   The 'Owner identifier' is then followed by a short content description
+   and explanation as to why it's encrypted. After the
+   'content/explanation' description, the actual encrypted block follows.
+
+   When an ID3v2 decoder encounters a "CRM" frame, it should send the
+   datablock to the 'plugin' with the corresponding 'owner identifier'
+   and expect to receive either a datablock with one or several ID3v2
+   frames after each other or an error. There may be more than one "CRM"
+   frames in a tag, but only one with the same 'owner identifier'.
+
+     Encrypted meta frame  "CRM"
+     Frame size            $xx xx xx
+     Owner identifier      <textstring> $00 (00)
+     Content/explanation   <textstring> $00 (00)
+     Encrypted datablock   <binary data>
+
+
+4.21.   Audio encryption
+
+   This frame indicates if the actual audio stream is encrypted, and by
+   whom. Since standardisation of such encrypion scheme is beyond this
+   document, all "CRA" frames begin with a terminated string with a
+   URL containing an email address, or a link to a location where an
+   email address can be found, that belongs to the organisation
+   responsible for this specific encrypted audio file. Questions
+   regarding the encrypted audio should be sent to the email address
+   specified. If a $00 is found directly after the 'Frame size' and the
+   audiofile indeed is encrypted, the whole file may be considered
+   useless.
+
+   After the 'Owner identifier', a pointer to an unencrypted part of the
+   audio can be specified. The 'Preview start' and 'Preview length' is
+   described in frames. If no part is unencrypted, these fields should be
+   left zeroed. After the 'preview length' field follows optionally a
+   datablock required for decryption of the audio. There may be more than
+   one "CRA" frames in a tag, but only one with the same 'Owner
+   identifier'.
+
+     Audio encryption   "CRA"
+     Frame size         $xx xx xx
+     Owner identifier   <textstring> $00 (00)
+     Preview start      $xx xx
+     Preview length     $xx xx
+     Encryption info    <binary data>
+
+
+4.22.   Linked information
+
+   To keep space waste as low as possible this frame may be used to link
+   information from another ID3v2 tag that might reside in another audio
+   file or alone in a binary file. It is recommended that this method is
+   only used when the files are stored on a CD-ROM or other circumstances
+   when the risk of file seperation is low. The frame contains a frame
+   identifier, which is the frame that should be linked into this tag, a
+   URL [URL] field, where a reference to the file where the frame is
+   given, and additional ID data, if needed. Data should be retrieved
+   from the first tag found in the file to which this link points. There
+   may be more than one "LNK" frame in a tag, but only one with the same
+   contents. A linked frame is to be considered as part of the tag and
+   has the same restrictions as if it was a physical part of the tag
+   (i.e. only one "REV" frame allowed, whether it's linked or not).
+   
+     Linked information  "LNK"
+     Frame size          $xx xx xx
+     Frame identifier    $xx xx xx
+     URL                 <textstring> $00 (00)
+     Additional ID data  <textstring(s)>
+   
+   Frames that may be linked and need no additional data are "IPL",
+   "MCI", "ETC", "LLT", "STC", "RVA", "EQU", "REV", "BUF", the text
+   information frames and the URL link frames.
+   
+   The "TXX", "PIC", "GEO", "CRM" and "CRA" frames may be linked with the
+   content descriptor as additional ID data.
+   
+   The "COM", "SLT" and "ULT" frames may be linked with three bytes of
+   language descriptor directly followed by a content descriptor as
+   additional ID data.
+
+
+5.   The 'unsynchronisation scheme'
+
+   The only purpose of the 'unsychronisation scheme' is to make the ID3v2
+   tag as compatible as possible with existing software. There is no use
+   in 'unsynchronising' tags if the file is only to be processed by new
+   software. Unsynchronisation may only be made with MPEG 2 layer I, II
+   and III and MPEG 2.5 files.
+
+   Whenever a false synchronisation is found within the tag, one zeroed
+   byte is inserted after the first false synchronisation byte. The
+   format of a correct sync that should be altered by ID3 encoders is as
+   follows:
+
+         %11111111 111xxxxx
+
+   And should be replaced with:
+
+         %11111111 00000000 111xxxxx
+
+   This has the side effect that all $FF 00 combinations have to be
+   altered, so they won't be affected by the decoding process. Therefore
+   all the $FF 00 combinations have to be replaced with the $FF 00 00
+   combination during the unsynchonisation.
+
+   To indicate usage of the unsynchronisation, the first bit in 'ID3
+   flags' should be set. This bit should only be set if the tag
+   contained a, now corrected, false synchronisation. The bit should
+   only be clear if the tag does not contain any false synchronisations.
+   
+   Do bear in mind, that if a compression scheme is used by the encoder,
+   the unsyncronisation scheme should be applied *afterwards*. When
+   decoding a compressed, 'unsyncronised' file, the 'unsyncronisation
+   scheme' should be parsed first, compression afterwards.
+
+
+6.   Copyright
+
+   Copyright (C) Martin Nilsson 1998. All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that a reference to this document is included on all
+   such copies and derivative works. However, this document itself may
+   not be modified in any way and reissued as the original document.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+   INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+   WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+7.   References
+
+   [CDDB] Compact Disc Data Base
+
+      <url:http://www.cddb.com>
+
+   [ISO-639-2] ISO/FDIS 639-2.
+   Codes for the representation of names of languages, Part 2: Alpha-3
+   code. Technical committee / subcommittee: TC 37 / SC 2
+
+   [ISO-8859-1] ISO/IEC DIS 8859-1.
+   8-bit single-byte coded graphic character sets, Part 1: Latin
+   alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2
+
+   [ISRC] ISO 3901:1986
+   International Standard Recording Code (ISRC).
+   Technical committee / subcommittee: TC 46 / SC 9
+
+   [JFIF] JPEG File Interchange Format, version 1.02
+
+      <url:http://www.w3.org/Graphics/JPEG/jfif.txt>
+      
+   [MIME] Freed, N.  and N. Borenstein,  "Multipurpose Internet Mail
+   Extensions (MIME) Part One: Format of Internet Message Bodies",
+   RFC 2045, November 1996.
+      <url:ftp://ftp.isi.edu/in-notes/rfc2045.txt>
+      
+   [MPEG] ISO/IEC 11172-3:1993.
+   Coding of moving pictures and associated audio for digital storage
+   media at up to about 1,5 Mbit/s, Part 3: Audio.
+   Technical committee / subcommittee: JTC 1 / SC 29
+   and   
+   ISO/IEC 13818-3:1995
+   Generic coding of moving pictures and associated audio information,
+   Part 3: Audio.
+   Technical committee / subcommittee: JTC 1 / SC 29
+   and   
+   ISO/IEC DIS 13818-3
+   Generic coding of moving pictures and associated audio information,
+   Part 3: Audio (Revision of ISO/IEC 13818-3:1995)
+
+
+   [PNG] Portable Network Graphics, version 1.0
+
+      <url:http://www.w3.org/TR/REC-png-multi.html>
+
+   [UNICODE] ISO/IEC 10646-1:1993.
+   Universal Multiple-Octet Coded Character Set (UCS), Part 1:
+   Architecture and Basic Multilingual Plane. Technical committee
+   / subcommittee: JTC 1 / SC 2
+
+      <url:http://www.unicode.org>
+
+   [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource
+   Locators (URL).", RFC 1738, December 1994.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
+
+
+8.   Appendix
+
+
+A.   Appendix A - ID3-Tag Specification V1.1
+
+   ID3-Tag Specification V1.1 (12 dec 1997) by Michael Mutschler
+   <amiga2@info2.rus.uni-stuttgart.de>, edited for space and clarity
+   reasons.
+
+
+A.1.   Overview
+
+   The ID3-Tag is an information field for MPEG Layer 3 audio files.
+   Since a standalone MP3 doesn't provide a method of storing other
+   information than those directly needed for replay reasons, the
+   ID3-tag was invented by Eric Kemp in 1996.
+
+   A revision from ID3v1 to ID3v1.1 was made by Michael Mutschler to
+   support track number information is described in A.4.
+
+
+A.2.   ID3v1 Implementation
+
+   The Information is stored in the last 128 bytes of an MP3. The Tag
+   has got the following fields, and the offsets given here, are from
+   0-127.
+
+     Field      Length    Offsets
+     Tag        3           0-2
+     Songname   30          3-32
+     Artist     30         33-62
+     Album      30         63-92
+     Year       4          93-96
+     Comment    30         97-126
+     Genre      1           127
+
+
+   The string-fields contain ASCII-data, coded in ISO-Latin 1 codepage.
+   Strings which are smaller than the field length are padded with zero-
+   bytes.
+
+     Tag: The tag is valid if this field contains the string "TAG". This
+        has to be uppercase!
+
+     Songname: This field contains the title of the MP3 (string as
+        above).
+
+     Artist: This field contains the artist of the MP3 (string as above).
+
+     Album: this field contains the album where the MP3 comes from
+        (string as above).
+
+     Year: this field contains the year when this song has originally
+        been released (string as above).
+
+     Comment: this field contains a comment for the MP3 (string as
+        above). Revision to this field has been made in ID3v1.1. See
+        A.4.
+
+     Genre: this byte contains the offset of a genre in a predefined
+        list the byte is treated as an unsigned byte. The offset is
+        starting from 0. See A.3.
+
+
+A.3.   Genre List
+
+   The following genres is defined in ID3v1 
+
+      0.Blues
+      1.Classic Rock
+      2.Country
+      3.Dance
+      4.Disco
+      5.Funk
+      6.Grunge
+      7.Hip-Hop
+      8.Jazz
+      9.Metal
+     10.New Age
+     11.Oldies
+     12.Other
+     13.Pop
+     14.R&B
+     15.Rap
+     16.Reggae
+     17.Rock
+     18.Techno
+     19.Industrial
+     20.Alternative
+     21.Ska
+     22.Death Metal
+     23.Pranks
+     24.Soundtrack
+     25.Euro-Techno
+     26.Ambient
+     27.Trip-Hop
+     28.Vocal
+     29.Jazz+Funk
+     30.Fusion
+     31.Trance
+     32.Classical
+     33.Instrumental
+     34.Acid
+     35.House
+     36.Game
+     37.Sound Clip
+     38.Gospel
+     39.Noise
+     40.AlternRock
+     41.Bass
+     42.Soul
+     43.Punk
+     44.Space
+     45.Meditative
+     46.Instrumental Pop
+     47.Instrumental Rock
+     48.Ethnic
+     49.Gothic
+     50.Darkwave
+     51.Techno-Industrial
+     52.Electronic
+     53.Pop-Folk
+     54.Eurodance
+     55.Dream
+     56.Southern Rock
+     57.Comedy
+     58.Cult
+     59.Gangsta
+     60.Top 40
+     61.Christian Rap
+     62.Pop/Funk
+     63.Jungle
+     64.Native American
+     65.Cabaret
+     66.New Wave
+     67.Psychadelic
+     68.Rave
+     69.Showtunes
+     70.Trailer
+     71.Lo-Fi
+     72.Tribal
+     73.Acid Punk
+     74.Acid Jazz
+     75.Polka
+     76.Retro
+     77.Musical
+     78.Rock & Roll
+     79.Hard Rock
+     
+   The following genres are Winamp extensions
+     
+     80.Folk
+     81.Folk-Rock
+     82.National Folk
+     83.Swing
+     84.Fast Fusion
+     85.Bebob
+     86.Latin
+     87.Revival
+     88.Celtic
+     89.Bluegrass
+     90.Avantgarde
+     91.Gothic Rock
+     92.Progressive Rock
+     93.Psychedelic Rock
+     94.Symphonic Rock
+     95.Slow Rock
+     96.Big Band
+     97.Chorus
+     98.Easy Listening
+     99.Acoustic
+    100.Humour
+    101.Speech
+    102.Chanson
+    103.Opera
+    104.Chamber Music
+    105.Sonata
+    106.Symphony
+    107.Booty Bass
+    108.Primus
+    109.Porn Groove
+    110.Satire
+    111.Slow Jam
+    112.Club
+    113.Tango
+    114.Samba
+    115.Folklore
+    116.Ballad
+    117.Power Ballad
+    118.Rhythmic Soul
+    119.Freestyle
+    120.Duet
+    121.Punk Rock
+    122.Drum Solo
+    123.A capella
+    124.Euro-House
+    125.Dance Hall
+
+
+A.4.   Track addition - ID3v1.1
+
+   In ID3v1.1, Michael Mutschler revised the specification of the
+   comment field in order to implement the track number. The new format
+   of the comment field is a 28 character string followed by a mandatory
+   null ($00) character and the original album tracknumber stored as an
+   unsigned byte-size integer. In such cases where the 29th byte is not
+   the null character or when the 30th is a null character, the
+   tracknumber is to be considered undefined.
+
+
+9.   Author's Address
+
+   Martin Nilsson
+   Rydsvägen 246 C. 30
+   S-584 34 Linköping
+   Sweden
+
+   Email: nilsson@id3.org
+
+   Co-authors:
+
+   Johan Sundström   Email: johan@id3.org
+
+
diff --git a/src/taglib/mpeg/id3v2/id3v2.3.0.txt b/src/taglib/mpeg/id3v2/id3v2.3.0.txt
new file mode 100644 (file)
index 0000000..b4ed763
--- /dev/null
@@ -0,0 +1,2022 @@
+Informal standard                                             M. Nilsson
+Document: id3v2.3.0.txt                                3rd February 1999
+
+
+                           ID3 tag version 2.3.0
+
+Status of this document
+
+   This document is an informal standard and replaces the ID3v2.2.0
+   standard [ID3v2]. The informal standard is released so that
+   implementors could have a set standard before a formal standard is
+   set. The formal standard will use another version or revision number
+   if not identical to what is described in this document. The contents
+   in this document may change for clarifications but never for added or
+   altered functionallity.
+
+   Distribution of this document is unlimited.
+
+
+Abstract
+
+   This document describes the ID3v2.3.0, which is a more developed
+   version of the ID3v2 informal standard [ID3v2] (version 2.2.0),
+   evolved from the ID3 tagging system. The ID3v2 offers a flexible way
+   of storing information about an audio file within itself to determine
+   its origin and contents. The information may be technical
+   information, such as equalisation curves, as well as related meta
+   information, such as title, performer, copyright etc.
+
+
+1.   Table of contents
+
+   2.   Conventions in this document
+   3.   ID3v2 overview
+     3.1.   ID3v2 header
+     3.2.   ID3v2 extended header
+     3.3.   ID3v2 frames overview
+       3.3.1.   Frame header flags
+       3.3.2.   Default flags
+   4.   Declared ID3v2 frames
+     4.1.   Unique file identifier
+     4.2.   Text information frames
+       4.2.1.   Text information frames - details
+       4.2.2.   User defined text information frame
+     4.3.   URL link frames
+       4.3.1.   URL link frames - details
+       4.3.2.   User defined URL link frame
+     4.4.   Involved people list
+     4.5.   Music CD Identifier
+     4.6.   Event timing codes
+     4.7.   MPEG location lookup table
+     4.8.   Synced tempo codes
+     4.9.   Unsychronised lyrics/text transcription
+     4.10.  Synchronised lyrics/text
+     4.11.  Comments
+     4.12.  Relative volume adjustment
+     4.13.  Equalisation
+     4.14.  Reverb
+     4.15.  Attached picture
+     4.16.  General encapsulated object
+     4.17.  Play counter
+     4.18.  Popularimeter
+     4.19.  Recommended buffer size
+     4.20.  Audio encryption
+     4.21.  Linked information
+     4.22.  Position synchronisation frame
+     4.23.  Terms of use
+     4.24.  Ownership frame
+     4.25.  Commercial frame
+     4.26.  Encryption method registration
+     4.27.  Group identification registration
+        4.28.  Private frame
+   5.   The 'unsynchronisation scheme'
+   6.   Copyright
+   7.   References
+   8.   Appendix
+     A.   Appendix A - Genre List from ID3v1
+   9.   Author's Address
+
+
+2.   Conventions in this document
+
+   In the examples, text within "" is a text string exactly as it
+   appears in a file. Numbers preceded with $ are hexadecimal and
+   numbers preceded with % are binary. $xx is used to indicate a byte
+   with unknown content. %x is used to indicate a bit with unknown
+   content. The most significant bit (MSB) of a byte is called 'bit 7'
+   and the least significant bit (LSB) is called 'bit 0'.
+
+   A tag is the whole tag described in this document. A frame is a block
+   of information in the tag. The tag consists of a header, frames and
+   optional padding. A field is a piece of information; one value, a
+   string etc. A numeric string is a string that consists of the
+   characters 0-9 only.
+
+
+3.   ID3v2 overview
+
+   The two biggest design goals were to be able to implement ID3v2
+   without disturbing old software too much and that ID3v2 should be
+   as flexible and expandable as possible.
+
+   The first criterion is met by the simple fact that the MPEG [MPEG]
+   decoding software uses a syncsignal, embedded in the audiostream, to
+   'lock on to' the audio. Since the ID3v2 tag doesn't contain a valid
+   syncsignal, no software will attempt to play the tag. If, for any
+   reason, coincidence make a syncsignal appear within the tag it will
+   be taken care of by the 'unsynchronisation scheme' described in
+   section 5.
+
+   The second criterion has made a more noticeable impact on the design
+   of the ID3v2 tag. It is constructed as a container for several
+   information blocks, called frames, whose format need not be known to
+   the software that encounters them. At the start of every frame there
+   is an identifier that explains the frames' format and content, and a
+   size descriptor that allows software to skip unknown frames.
+
+   If a total revision of the ID3v2 tag should be needed, there is a
+   version number and a size descriptor in the ID3v2 header.
+
+   The ID3 tag described in this document is mainly targeted at files
+   encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III
+   and MPEG-2.5, but may work with other types of encoded audio.
+
+   The bitorder in ID3v2 is most significant bit first (MSB). The
+   byteorder in multibyte numbers is most significant byte first (e.g.
+   $12345678 would be encoded $12 34 56 78).
+
+   It is permitted to include padding after all the final frame (at the
+   end of the ID3 tag), making the size of all the frames together
+   smaller than the size given in the head of the tag. A possible
+   purpose of this padding is to allow for adding a few additional
+   frames or enlarge existing frames within the tag without having to
+   rewrite the entire file. The value of the padding bytes must be $00.
+
+
+3.1.   ID3v2 header
+
+   The ID3v2 tag header, which should be the first information in the
+   file, is 10 bytes as follows:
+
+     ID3v2/file identifier      "ID3"
+     ID3v2 version              $03 00
+     ID3v2 flags                %abc00000
+     ID3v2 size             4 * %0xxxxxxx
+
+   The first three bytes of the tag are always "ID3" to indicate that
+   this is an ID3v2 tag, directly followed by the two version bytes. The
+   first byte of ID3v2 version is it's major version, while the second
+   byte is its revision number. In this case this is ID3v2.3.0. All
+   revisions are backwards compatible while major versions are not. If
+   software with ID3v2.2.0 and below support should encounter version
+   three or higher it should simply ignore the whole tag. Version and
+   revision will never be $FF.
+
+   The version is followed by one the ID3v2 flags field, of which
+   currently only three flags are used.
+
+
+   a - Unsynchronisation
+
+     Bit 7 in the 'ID3v2 flags' indicates whether or not
+     unsynchronisation is used (see section 5 for details); a set bit
+     indicates usage.
+
+
+   b - Extended header
+
+     The second bit (bit 6) indicates whether or not the header is
+     followed by an extended header. The extended header is described in
+     section 3.2.
+
+
+   c - Experimental indicator
+
+     The third bit (bit 5) should be used as an 'experimental
+     indicator'. This flag should always be set when the tag is in an
+     experimental stage.
+
+   All the other flags should be cleared. If one of these undefined
+   flags are set that might mean that the tag is not readable for a
+   parser that does not know the flags function.
+
+   The ID3v2 tag size is encoded with four bytes where the most
+   significant bit (bit 7) is set to zero in every byte, making a total
+   of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is
+   represented as $00 00 02 01.
+
+   The ID3v2 tag size is the size of the complete tag after
+   unsychronisation, including padding, excluding the header but not
+   excluding the extended header (total tag size - 10). Only 28 bits
+   (representing up to 256MB) are used in the size description to avoid
+   the introducuction of 'false syncsignals'.
+
+   An ID3v2 tag can be detected with the following pattern:
+     $49 44 33 yy yy xx zz zz zz zz
+   Where yy is less than $FF, xx is the 'flags' byte and zz is less than
+   $80.
+
+
+3.2.   ID3v2 extended header
+
+   The extended header contains information that is not vital to the
+   correct parsing of the tag information, hence the extended header is
+   optional.
+
+     Extended header size   $xx xx xx xx
+     Extended Flags         $xx xx
+     Size of padding        $xx xx xx xx
+
+   Where the 'Extended header size', currently 6 or 10 bytes, excludes
+   itself. The 'Size of padding' is simply the total tag size excluding
+   the frames and the headers, in other words the padding. The extended
+   header is considered separate from the header proper, and as such is
+   subject to unsynchronisation.
+
+   The extended flags are a secondary flag set which describes further
+   attributes of the tag. These attributes are currently defined as
+   follows
+
+     %x0000000 00000000
+
+
+   x - CRC data present
+
+     If this flag is set four bytes of CRC-32 data is appended to the
+     extended header. The CRC should be calculated before
+     unsynchronisation on the data between the extended header and the
+     padding, i.e. the frames and only the frames.
+
+        Total frame CRC        $xx xx xx xx
+
+
+3.3.   ID3v2 frame overview
+
+   As the tag consists of a tag header and a tag body with one or more
+   frames, all the frames consists of a frame header followed by one or
+   more fields containing the actual information. The layout of the
+   frame header:
+
+     Frame ID   $xx xx xx xx  (four characters)
+     Size       $xx xx xx xx
+     Flags      $xx xx
+
+   The frame ID made out of the characters capital A-Z and 0-9.
+   Identifiers beginning with "X", "Y" and "Z" are for experimental use
+   and free for everyone to use, without the need to set the
+   experimental bit in the tag header. Have in mind that someone else
+   might have used the same identifier as you. All other identifiers are
+   either used or reserved for future use.
+
+   The frame ID is followed by a size descriptor, making a total header
+   size of ten bytes in every frame. The size is calculated as frame
+   size excluding frame header (frame size - 10).
+
+   In the frame header the size descriptor is followed by two flags
+   bytes. These flags are described in section 3.3.1.
+
+   There is no fixed order of the frames' appearance in the tag,
+   although it is desired that the frames are arranged in order of
+   significance concerning the recognition of the file. An example of
+   such order: UFID, TIT2, MCDI, TRCK ...
+
+   A tag must contain at least one frame. A frame must be at least 1
+   byte big, excluding the header.
+
+   If nothing else is said a string is represented as ISO-8859-1
+   [ISO-8859-1] characters in the range $20 - $FF. Such strings are
+   represented as <text string>, or <full text string> if newlines are
+   allowed, in the frame descriptions. All Unicode strings [UNICODE] use
+   16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). Unicode strings
+   must begin with the Unicode BOM ($FF FE or $FE FF) to identify the
+   byte order.
+
+   All numeric strings and URLs [URL] are always encoded as ISO-8859-1.
+   Terminated strings are terminated with $00 if encoded with ISO-8859-1
+   and $00 00 if encoded as unicode. If nothing else is said newline
+   character is forbidden. In ISO-8859-1 a new line is represented, when
+   allowed, with $0A only. Frames that allow different types of text
+   encoding have a text encoding description byte directly after the
+   frame size. If ISO-8859-1 is used this byte should be $00, if Unicode
+   is used it should be $01. Strings dependent on encoding is
+   represented as <text string according to encoding>, or <full text
+   string according to encoding> if newlines are allowed.  Any empty
+   Unicode strings which are NULL-terminated may have the Unicode BOM
+   followed by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).
+
+   The three byte language field is used to describe the language of the
+   frame's content, according to ISO-639-2 [ISO-639-2].
+
+   All URLs [URL] may be relative, e.g. "picture.png", "../doc.txt".
+
+   If a frame is longer than it should be, e.g. having more fields than
+   specified in this document, that indicates that additions to the
+   frame have been made in a later version of the ID3v2 standard. This
+   is reflected by the revision number in the header of the tag.
+
+
+3.3.1.   Frame header flags
+
+   In the frame header the size descriptor is followed by two flags
+   bytes. All unused flags must be cleared. The first byte is for
+   'status messages' and the second byte is for encoding purposes. If an
+   unknown flag is set in the first byte the frame may not be changed
+   without the bit cleared. If an unknown flag is set in the second byte
+   it is likely to not be readable. The flags field is defined as
+   follows.
+
+     %abc00000 %ijk00000
+
+
+   a - Tag alter preservation
+
+     This flag tells the software what to do with this frame if it is
+     unknown and the tag is altered in any way. This applies to all
+     kinds of alterations, including adding more padding and reordering
+     the frames.
+
+     0     Frame should be preserved.
+     1     Frame should be discarded.
+
+
+   b - File alter preservation
+
+     This flag tells the software what to do with this frame if it is
+     unknown and the file, excluding the tag, is altered. This does not
+     apply when the audio is completely replaced with other audio data.
+
+     0     Frame should be preserved.
+     1     Frame should be discarded.
+
+
+   c - Read only
+
+      This flag, if set, tells the software that the contents of this
+      frame is intended to be read only. Changing the contents might
+      break something, e.g. a signature. If the contents are changed,
+      without knowledge in why the frame was flagged read only and
+      without taking the proper means to compensate, e.g. recalculating
+      the signature, the bit should be cleared.
+
+
+   i - Compression
+
+      This flag indicates whether or not the frame is compressed.
+
+      0     Frame is not compressed.
+      1     Frame is compressed using zlib [zlib] with 4 bytes for
+            'decompressed size' appended to the frame header.
+
+
+   j - Encryption
+
+      This flag indicates wether or not the frame is enrypted. If set
+      one byte indicating with which method it was encrypted will be
+      appended to the frame header. See section 4.26. for more
+      information about encryption method registration.
+
+      0     Frame is not encrypted.
+      1     Frame is encrypted.
+
+
+   k - Grouping identity
+
+      This flag indicates whether or not this frame belongs in a group
+      with other frames. If set a group identifier byte is added to the
+      frame header. Every frame with the same group identifier belongs
+      to the same group.
+
+      0     Frame does not contain group information
+      1     Frame contains group information
+
+
+   Some flags indicates that the frame header is extended with
+   additional information. This information will be added to the frame
+   header in the same order as the flags indicating the additions. I.e.
+   the four bytes of decompressed size will preceed the encryption
+   method byte. These additions to the frame header, while not included
+   in the frame header size but are included in the 'frame size' field,
+   are not subject to encryption or compression.
+
+
+3.3.2.   Default flags
+
+   The default settings for the frames described in this document can be
+   divided into the following classes. The flags may be set differently
+   if found more suitable by the software.
+
+    1. Discarded if tag is altered, discarded if file is altered.
+
+       None.
+
+    2. Discarded if tag is altered, preserved if file is altered.
+
+       None.
+
+    3. Preserved if tag is altered, discarded if file is altered.
+
+       AENC, ETCO, EQUA, MLLT, POSS, SYLT, SYTC, RVAD, TENC, TLEN, TSIZ
+
+    4. Preserved if tag is altered, preserved if file is altered.
+
+       The rest of the frames.
+
+
+4.   Declared ID3v2 frames
+
+   The following frames are declared in this draft.
+
+  4.21  AENC Audio encryption
+  4.15  APIC Attached picture
+
+  4.11  COMM Comments
+  4.25  COMR Commercial frame
+
+  4.26  ENCR Encryption method registration
+  4.13  EQUA Equalization
+  4.6   ETCO Event timing codes
+
+  4.16  GEOB General encapsulated object
+  4.27  GRID Group identification registration
+
+  4.4   IPLS Involved people list
+
+  4.21  LINK Linked information
+
+  4.5   MCDI Music CD identifier
+  4.7   MLLT MPEG location lookup table
+
+  4.24  OWNE Ownership frame
+
+  4.28. PRIV Private frame
+  4.17  PCNT Play counter
+  4.18  POPM Popularimeter
+  4.22  POSS Position synchronisation frame
+
+  4.19  RBUF Recommended buffer size
+  4.12  RVAD Relative volume adjustment
+  4.14  RVRB Reverb
+
+  4.10  SYLT Synchronized lyric/text
+  4.8   SYTC Synchronized tempo codes
+
+  4.2.1 TALB Album/Movie/Show title
+  4.2.1 TBPM BPM (beats per minute)
+  4.2.1 TCOM Composer
+  4.2.1 TCON Content type
+  4.2.1 TCOP Copyright message
+  4.2.1 TDAT Date
+  4.2.1 TDLY Playlist delay
+  4.2.1 TENC Encoded by
+  4.2.1 TEXT Lyricist/Text writer
+  4.2.1 TFLT File type
+  4.2.1 TIME Time
+  4.2.1 TIT1 Content group description
+  4.2.1 TIT2 Title/songname/content description
+  4.2.1 TIT3 Subtitle/Description refinement
+  4.2.1 TKEY Initial key
+  4.2.1 TLAN Language(s)
+  4.2.1 TLEN Length
+  4.2.1 TMED Media type
+  4.2.1 TOAL Original album/movie/show title
+  4.2.1 TOFN Original filename
+  4.2.1 TOLY Original lyricist(s)/text writer(s)
+  4.2.1 TOPE Original artist(s)/performer(s)
+  4.2.1 TORY Original release year
+  4.2.1 TOWN File owner/licensee
+  4.2.1 TPE1 Lead performer(s)/Soloist(s)
+  4.2.1 TPE2 Band/orchestra/accompaniment
+  4.2.1 TPE3 Conductor/performer refinement
+  4.2.1 TPE4 Interpreted, remixed, or otherwise modified by
+  4.2.1 TPOS Part of a set
+  4.2.1 TPUB Publisher
+  4.2.1 TRCK Track number/Position in set
+  4.2.1 TRDA Recording dates
+  4.2.1 TRSN Internet radio station name
+  4.2.1 TRSO Internet radio station owner
+  4.2.1 TSIZ Size
+  4.2.1 TSRC ISRC (international standard recording code)
+  4.2.1 TSSE Software/Hardware and settings used for encoding
+  4.2.1 TYER Year
+  4.2.2 TXXX User defined text information frame
+
+  4.1   UFID Unique file identifier
+  4.23  USER Terms of use
+  4.9   USLT Unsychronized lyric/text transcription
+
+  4.3.1 WCOM Commercial information
+  4.3.1 WCOP Copyright/Legal information
+  4.3.1 WOAF Official audio file webpage
+  4.3.1 WOAR Official artist/performer webpage
+  4.3.1 WOAS Official audio source webpage
+  4.3.1 WORS Official internet radio station homepage
+  4.3.1 WPAY Payment
+  4.3.1 WPUB Publishers official webpage
+  4.3.2 WXXX User defined URL link frame
+
+
+4.1.   Unique file identifier
+
+   This frame's purpose is to be able to identify the audio file in a
+   database that may contain more information relevant to the content.
+   Since standardisation of such a database is beyond this document, all
+   frames begin with a null-terminated string with a URL [URL]
+   containing an email address, or a link to a location where an email
+   address can be found, that belongs to the organisation responsible
+   for this specific database implementation. Questions regarding the
+   database should be sent to the indicated email address. The URL
+   should not be used for the actual database queries. The string
+   "<a href="http://www.id3.org/dummy/ufid.html">http://www.id3.org/dummy/ufid.html</a>" should be used for tests.
+   Software that isn't told otherwise may safely remove such frames. The
+   'Owner identifier' must be non-empty (more than just a termination).
+   The 'Owner identifier' is then followed by the actual identifier,
+   which may be up to 64 bytes. There may be more than one "UFID" frame
+   in a tag, but only one with the same 'Owner identifier'.
+
+     <Header for 'Unique file identifier', ID: "UFID">
+     Owner identifier        <text string> $00
+     Identifier              <up to 64 bytes binary data>
+
+
+4.2.   Text information frames
+
+   The text information frames are the most important frames, containing
+   information like artist, album and more. There may only be one text
+   information frame of its kind in an tag. If the textstring is
+   followed by a termination ($00 (00)) all the following information
+   should be ignored and not be displayed. All text frame identifiers
+   begin with "T". Only text frame identifiers begin with "T", with the
+   exception of the "TXXX" frame. All the text information frames have
+   the following format:
+
+     <Header for 'Text information frame', ID: "T000" - "TZZZ",
+     excluding "TXXX" described in 4.2.2.>
+     Text encoding                $xx
+     Information                  <text string according to encoding>
+
+
+4.2.1.   Text information frames - details
+
+  TALB
+   The 'Album/Movie/Show title' frame is intended for the title of the
+   recording(/source of sound) which the audio in the file is taken
+   from.
+
+  TBPM
+   The 'BPM' frame contains the number of beats per minute in the
+   mainpart of the audio. The BPM is an integer and represented as a
+   numerical string.
+
+  TCOM
+   The 'Composer(s)' frame is intended for the name of the composer(s).
+   They are seperated with the "/" character.
+
+  TCON
+   The 'Content type', which previously was stored as a one byte numeric
+   value only, is now a numeric string. You may use one or several of
+   the types as ID3v1.1 did or, since the category list would be
+   impossible to maintain with accurate and up to date categories,
+   define your own.
+
+   References to the ID3v1 genres can be made by, as first byte, enter
+   "(" followed by a number from the genres list (appendix A.) and
+   ended with a ")" character. This is optionally followed by a
+   refinement, e.g. "(21)" or "(4)Eurodisco". Several references can be
+   made in the same frame, e.g. "(51)(39)". If the refinement should
+   begin with a "(" character it should be replaced with "((", e.g. "((I
+   can figure out any genre)" or "(55)((I think...)". The following new
+   content types is defined in ID3v2 and is implemented in the same way
+   as the numerig content types, e.g. "(RX)".
+
+     RX  Remix
+     CR  Cover
+
+  TCOP
+   The 'Copyright message' frame, which must begin with a year and a
+   space character (making five characters), is intended for the
+   copyright holder of the original sound, not the audio file itself.
+   The absence of this frame means only that the copyright information
+   is unavailable or has been removed, and must not be interpreted to
+   mean that the sound is public domain. Every time this field is
+   displayed the field must be preceded with "Copyright " (C) " ", where
+   (C) is one character showing a C in a circle.
+
+  TDAT
+   The 'Date' frame is a numeric string in the DDMM format containing
+   the date for the recording. This field is always four characters
+   long.
+
+  TDLY
+   The 'Playlist delay' defines the numbers of milliseconds of silence
+   between every song in a playlist. The player should use the "ETC"
+   frame, if present, to skip initial silence and silence at the end of
+   the audio to match the 'Playlist delay' time. The time is represented
+   as a numeric string.
+
+  TENC
+   The 'Encoded by' frame contains the name of the person or
+   organisation that encoded the audio file. This field may contain a
+   copyright message, if the audio file also is copyrighted by the
+   encoder.
+
+  TEXT
+   The 'Lyricist(s)/Text writer(s)' frame is intended for the writer(s)
+   of the text or lyrics in the recording. They are seperated with the
+   "/" character.
+
+  TFLT
+   The 'File type' frame indicates which type of audio this tag defines.
+   The following type and refinements are defined:
+
+     MPG    MPEG Audio
+       /1     MPEG 1/2 layer I
+       /2     MPEG 1/2 layer II
+       /3     MPEG 1/2 layer III
+       /2.5   MPEG 2.5
+       /AAC   Advanced audio compression
+     VQF    Transform-domain Weighted Interleave Vector Quantization
+     PCM    Pulse Code Modulated audio
+
+   but other types may be used, not for these types though. This is used
+   in a similar way to the predefined types in the "TMED" frame, but
+   without parentheses. If this frame is not present audio type is
+   assumed to be "MPG".
+
+  TIME
+   The 'Time' frame is a numeric string in the HHMM format containing
+   the time for the recording. This field is always four characters
+   long.
+
+  TIT1
+   The 'Content group description' frame is used if the sound belongs to
+   a larger category of sounds/music. For example, classical music is
+   often sorted in different musical sections (e.g. "Piano Concerto",
+   "Weather - Hurricane").
+
+  TIT2
+   The 'Title/Songname/Content description' frame is the actual name of
+   the piece (e.g. "Adagio", "Hurricane Donna").
+
+  TIT3
+   The 'Subtitle/Description refinement' frame is used for information
+   directly related to the contents title (e.g. "Op. 16" or "Performed
+   live at Wembley").
+
+  TKEY
+   The 'Initial key' frame contains the musical key in which the sound
+   starts. It is represented as a string with a maximum length of three
+   characters. The ground keys are represented with "A","B","C","D","E",
+   "F" and "G" and halfkeys represented with "b" and "#". Minor is
+   represented as "m". Example "Cbm". Off key is represented with an "o"
+   only.
+
+  TLAN
+   The 'Language(s)' frame should contain the languages of the text or
+   lyrics spoken or sung in the audio. The language is represented with
+   three characters according to ISO-639-2. If more than one language is
+   used in the text their language codes should follow according to
+   their usage.
+
+  TLEN
+   The 'Length' frame contains the length of the audiofile in
+   milliseconds, represented as a numeric string.
+
+  TMED
+   The 'Media type' frame describes from which media the sound
+   originated. This may be a text string or a reference to the
+   predefined media types found in the list below. References are made
+   within "(" and ")" and are optionally followed by a text refinement,
+   e.g. "(MC) with four channels". If a text refinement should begin
+   with a "(" character it should be replaced with "((" in the same way
+   as in the "TCO" frame. Predefined refinements is appended after the
+   media type, e.g. "(CD/A)" or "(VID/PAL/VHS)".
+
+    DIG    Other digital media
+      /A    Analog transfer from media
+
+    ANA    Other analog media
+      /WAC  Wax cylinder
+      /8CA  8-track tape cassette
+
+    CD     CD
+      /A    Analog transfer from media
+      /DD   DDD
+      /AD   ADD
+      /AA   AAD
+
+    LD     Laserdisc
+      /A     Analog transfer from media
+
+    TT     Turntable records
+      /33    33.33 rpm
+      /45    45 rpm
+      /71    71.29 rpm
+      /76    76.59 rpm
+      /78    78.26 rpm
+      /80    80 rpm
+
+    MD     MiniDisc
+      /A    Analog transfer from media
+
+    DAT    DAT
+      /A    Analog transfer from media
+      /1    standard, 48 kHz/16 bits, linear
+      /2    mode 2, 32 kHz/16 bits, linear
+      /3    mode 3, 32 kHz/12 bits, nonlinear, low speed
+      /4    mode 4, 32 kHz/12 bits, 4 channels
+      /5    mode 5, 44.1 kHz/16 bits, linear
+      /6    mode 6, 44.1 kHz/16 bits, 'wide track' play
+
+    DCC    DCC
+      /A    Analog transfer from media
+
+    DVD    DVD
+      /A    Analog transfer from media
+
+    TV     Television
+      /PAL    PAL
+      /NTSC   NTSC
+      /SECAM  SECAM
+
+    VID    Video
+      /PAL    PAL
+      /NTSC   NTSC
+      /SECAM  SECAM
+      /VHS    VHS
+      /SVHS   S-VHS
+      /BETA   BETAMAX
+
+    RAD    Radio
+      /FM   FM
+      /AM   AM
+      /LW   LW
+      /MW   MW
+
+    TEL    Telephone
+      /I    ISDN
+
+    MC     MC (normal cassette)
+      /4    4.75 cm/s (normal speed for a two sided cassette)
+      /9    9.5 cm/s
+      /I    Type I cassette (ferric/normal)
+      /II   Type II cassette (chrome)
+      /III  Type III cassette (ferric chrome)
+      /IV   Type IV cassette (metal)
+
+    REE    Reel
+      /9    9.5 cm/s
+      /19   19 cm/s
+      /38   38 cm/s
+      /76   76 cm/s
+      /I    Type I cassette (ferric/normal)
+      /II   Type II cassette (chrome)
+      /III  Type III cassette (ferric chrome)
+      /IV   Type IV cassette (metal)
+
+  TOAL
+   The 'Original album/movie/show title' frame is intended for the title
+   of the original recording (or source of sound), if for example the
+   music in the file should be a cover of a previously released song.
+
+  TOFN
+   The 'Original filename' frame contains the preferred filename for the
+   file, since some media doesn't allow the desired length of the
+   filename. The filename is case sensitive and includes its suffix.
+
+  TOLY
+   The 'Original lyricist(s)/text writer(s)' frame is intended for the
+   text writer(s) of the original recording, if for example the music in
+   the file should be a cover of a previously released song. The text
+   writers are seperated with the "/" character.
+
+  TOPE
+   The 'Original artist(s)/performer(s)' frame is intended for the
+   performer(s) of the original recording, if for example the music in
+   the file should be a cover of a previously released song. The
+   performers are seperated with the "/" character.
+
+  TORY
+   The 'Original release year' frame is intended for the year when the
+   original recording, if for example the music in the file should be a
+   cover of a previously released song, was released. The field is
+   formatted as in the "TYER" frame.
+
+  TOWN
+   The 'File owner/licensee' frame contains the name of the owner or
+   licensee of the file and it's contents.
+
+  TPE1
+   The 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group' is
+   used for the main artist(s). They are seperated with the "/"
+   character.
+
+  TPE2
+   The 'Band/Orchestra/Accompaniment' frame is used for additional
+   information about the performers in the recording.
+
+  TPE3
+   The 'Conductor' frame is used for the name of the conductor.
+
+  TPE4
+   The 'Interpreted, remixed, or otherwise modified by' frame contains
+   more information about the people behind a remix and similar
+   interpretations of another existing piece.
+
+  TPOS
+   The 'Part of a set' frame is a numeric string that describes which
+   part of a set the audio came from. This frame is used if the source
+   described in the "TALB" frame is divided into several mediums, e.g. a
+   double CD. The value may be extended with a "/" character and a
+   numeric string containing the total number of parts in the set. E.g.
+   "1/2".
+
+  TPUB
+   The 'Publisher' frame simply contains the name of the label or
+   publisher.
+
+  TRCK
+   The 'Track number/Position in set' frame is a numeric string
+   containing the order number of the audio-file on its original
+   recording. This may be extended with a "/" character and a numeric
+   string containing the total numer of tracks/elements on the original
+   recording. E.g. "4/9".
+
+  TRDA
+   The 'Recording dates' frame is a intended to be used as complement to
+   the "TYER", "TDAT" and "TIME" frames. E.g. "4th-7th June, 12th June"
+   in combination with the "TYER" frame.
+
+  TRSN
+   The 'Internet radio station name' frame contains the name of the
+   internet radio station from which the audio is streamed.
+
+  TRSO
+   The 'Internet radio station owner' frame contains the name of the
+   owner of the internet radio station from which the audio is
+   streamed.
+
+  TSIZ
+   The 'Size' frame contains the size of the audiofile in bytes,
+   excluding the ID3v2 tag, represented as a numeric string.
+
+  TSRC
+   The 'ISRC' frame should contain the International Standard Recording
+   Code [ISRC] (12 characters).
+
+  TSSE
+   The 'Software/Hardware and settings used for encoding' frame
+   includes the used audio encoder and its settings when the file was
+   encoded. Hardware refers to hardware encoders, not the computer on
+   which a program was run.
+
+  TYER
+   The 'Year' frame is a numeric string with a year of the recording.
+   This frames is always four characters long (until the year 10000).
+
+
+4.2.2.   User defined text information frame
+
+   This frame is intended for one-string text information concerning the
+   audiofile in a similar way to the other "T"-frames. The frame body
+   consists of a description of the string, represented as a terminated
+   string, followed by the actual string. There may be more than one
+   "TXXX" frame in each tag, but only one with the same description.
+
+     <Header for 'User defined text information frame', ID: "TXXX">
+     Text encoding     $xx
+     Description       <text string according to encoding> $00 (00)
+     Value             <text string according to encoding>
+
+
+4.3.   URL link frames
+
+   With these frames dynamic data such as webpages with touring
+   information, price information or plain ordinary news can be added to
+   the tag. There may only be one URL [URL] link frame of its kind in an
+   tag, except when stated otherwise in the frame description. If the
+   textstring is followed by a termination ($00 (00)) all the following
+   information should be ignored and not be displayed. All URL link
+   frame identifiers begins with "W". Only URL link frame identifiers
+   begins with "W". All URL link frames have the following format:
+
+     <Header for 'URL link frame', ID: "W000" - "WZZZ", excluding "WXXX"
+     described in 4.3.2.>
+     URL              <text string>
+
+
+4.3.1.   URL link frames - details
+
+  WCOM
+   The 'Commercial information' frame is a URL pointing at a webpage
+   with information such as where the album can be bought. There may be
+   more than one "WCOM" frame in a tag, but not with the same content.
+
+  WCOP
+   The 'Copyright/Legal information' frame is a URL pointing at a
+   webpage where the terms of use and ownership of the file is
+   described.
+
+  WOAF
+   The 'Official audio file webpage' frame is a URL pointing at a file
+   specific webpage.
+
+  WOAR
+   The 'Official artist/performer webpage' frame is a URL pointing at
+   the artists official webpage. There may be more than one "WOAR" frame
+   in a tag if the audio contains more than one performer, but not with
+   the same content.
+
+  WOAS
+   The 'Official audio source webpage' frame is a URL pointing at the
+   official webpage for the source of the audio file, e.g. a movie.
+
+  WORS
+   The 'Official internet radio station homepage' contains a URL
+   pointing at the homepage of the internet radio station.
+
+  WPAY
+   The 'Payment' frame is a URL pointing at a webpage that will handle
+   the process of paying for this file.
+
+  WPUB
+   The 'Publishers official webpage' frame is a URL pointing at the
+   official wepage for the publisher.
+
+
+4.3.2.   User defined URL link frame
+
+   This frame is intended for URL [URL] links concerning the audiofile
+   in a similar way to the other "W"-frames. The frame body consists
+   of a description of the string, represented as a terminated string,
+   followed by the actual URL. The URL is always encoded with ISO-8859-1
+   [ISO-8859-1]. There may be more than one "WXXX" frame in each tag,
+   but only one with the same description.
+
+     <Header for 'User defined URL link frame', ID: "WXXX">
+     Text encoding     $xx
+     Description       <text string according to encoding> $00 (00)
+     URL               <text string>
+
+
+4.4.   Involved people list
+
+   Since there might be a lot of people contributing to an audio file in
+   various ways, such as musicians and technicians, the 'Text
+   information frames' are often insufficient to list everyone involved
+   in a project. The 'Involved people list' is a frame containing the
+   names of those involved, and how they were involved. The body simply
+   contains a terminated string with the involvement directly followed
+   by a terminated string with the involvee followed by a new
+   involvement and so on. There may only be one "IPLS" frame in each
+   tag.
+
+     <Header for 'Involved people list', ID: "IPLS">
+     Text encoding          $xx
+     People list strings    <text strings according to encoding>
+
+
+4.5.   Music CD identifier
+
+   This frame is intended for music that comes from a CD, so that the CD
+   can be identified in databases such as the CDDB [CDDB]. The frame
+   consists of a binary dump of the Table Of Contents, TOC, from the CD,
+   which is a header of 4 bytes and then 8 bytes/track on the CD plus 8
+   bytes for the 'lead out' making a maximum of 804 bytes. The offset to
+   the beginning of every track on the CD should be described with a
+   four bytes absolute CD-frame address per track, and not with absolute
+   time. This frame requires a present and valid "TRCK" frame, even if
+   the CD's only got one track. There may only be one "MCDI" frame in
+   each tag.
+
+     <Header for 'Music CD identifier', ID: "MCDI">
+     CD TOC                <binary data>
+
+
+4.6.   Event timing codes
+
+   This frame allows synchronisation with key events in a song or sound.
+   The header is:
+
+     <Header for 'Event timing codes', ID: "ETCO">
+     Time stamp format    $xx
+
+   Where time stamp format is:
+
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Abolute time means that every stamp contains the time from the
+   beginning of the file.
+
+   Followed by a list of key events in the following format:
+
+     Type of event   $xx
+     Time stamp      $xx (xx ...)
+
+   The 'Time stamp' is set to zero if directly at the beginning of the
+   sound or after the previous event. All events should be sorted in
+   chronological order. The type of event is as follows:
+
+     $00  padding (has no meaning)
+     $01  end of initial silence
+     $02  intro start
+     $03  mainpart start
+     $04  outro start
+     $05  outro end
+     $06  verse start
+     $07  refrain start
+     $08  interlude start
+     $09  theme start
+     $0A  variation start
+     $0B  key change
+     $0C  time change
+     $0D  momentary unwanted noise (Snap, Crackle & Pop)
+     $0E  sustained noise
+     $0F  sustained noise end
+     $10  intro end
+     $11  mainpart end
+     $12  verse end
+     $13  refrain end
+     $14  theme end
+
+     $15-$DF  reserved for future use
+
+     $E0-$EF  not predefined sync 0-F
+
+     $F0-$FC  reserved for future use
+
+     $FD  audio end (start of silence)
+     $FE  audio file ends
+     $FF  one more byte of events follows (all the following bytes with
+          the value $FF have the same function)
+
+   Terminating the start events such as "intro start" is not required.
+   The 'Not predefined sync's ($E0-EF) are for user events. You might
+   want to synchronise your music to something, like setting of an
+   explosion on-stage, turning on your screensaver etc.
+
+   There may only be one "ETCO" frame in each tag.
+
+
+4.7.   MPEG location lookup table
+
+   To increase performance and accuracy of jumps within a MPEG [MPEG]
+   audio file, frames with timecodes in different locations in the file
+   might be useful. The ID3v2 frame includes references that the
+   software can use to calculate positions in the file. After the frame
+   header is a descriptor of how much the 'frame counter' should
+   increase for every reference. If this value is two then the first
+   reference points out the second frame, the 2nd reference the 4th
+   frame, the 3rd reference the 6th frame etc. In a similar way the
+   'bytes between reference' and 'milliseconds between reference' points
+   out bytes and milliseconds respectively.
+
+   Each reference consists of two parts; a certain number of bits, as
+   defined in 'bits for bytes deviation', that describes the difference
+   between what is said in 'bytes between reference' and the reality and
+   a certain number of bits, as defined in 'bits for milliseconds
+   deviation', that describes the difference between what is said in
+   'milliseconds between reference' and the reality. The number of bits
+   in every reference, i.e. 'bits for bytes deviation'+'bits for
+   milliseconds deviation', must be a multiple of four. There may only
+   be one "MLLT" frame in each tag.
+
+     <Header for 'Location lookup table', ID: "MLLT">
+     MPEG frames between reference  $xx xx
+     Bytes between reference        $xx xx xx
+     Milliseconds between reference $xx xx xx
+     Bits for bytes deviation       $xx
+     Bits for milliseconds dev.     $xx
+
+   Then for every reference the following data is included;
+
+     Deviation in bytes         %xxx....
+     Deviation in milliseconds  %xxx....
+
+
+4.8.   Synchronised tempo codes
+
+   For a more accurate description of the tempo of a musical piece this
+   frame might be used. After the header follows one byte describing
+   which time stamp format should be used. Then follows one or more
+   tempo codes. Each tempo code consists of one tempo part and one time
+   part. The tempo is in BPM described with one or two bytes. If the
+   first byte has the value $FF, one more byte follows, which is added
+   to the first giving a range from 2 - 510 BPM, since $00 and $01 is
+   reserved. $00 is used to describe a beat-free time period, which is
+   not the same as a music-free time period. $01 is used to indicate one
+   single beat-stroke followed by a beat-free period.
+
+   The tempo descriptor is followed by a time stamp. Every time the
+   tempo in the music changes, a tempo descriptor may indicate this for
+   the player. All tempo descriptors should be sorted in chronological
+   order. The first beat-stroke in a time-period is at the same time as
+   the beat description occurs. There may only be one "SYTC" frame in
+   each tag.
+
+     <Header for 'Synchronised tempo codes', ID: "SYTC">
+     Time stamp format   $xx
+     Tempo data          <binary data>
+
+   Where time stamp format is:
+
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Abolute time means that every stamp contains the time from the
+   beginning of the file.
+
+
+4.9.   Unsychronised lyrics/text transcription
+
+   This frame contains the lyrics of the song or a text transcription of
+   other vocal activities. The head includes an encoding descriptor and
+   a content descriptor. The body consists of the actual text. The
+   'Content descriptor' is a terminated string. If no descriptor is
+   entered, 'Content descriptor' is $00 (00) only. Newline characters
+   are allowed in the text. There may be more than one 'Unsynchronised
+   lyrics/text transcription' frame in each tag, but only one with the
+   same language and content descriptor.
+
+     <Header for 'Unsynchronised lyrics/text transcription', ID: "USLT">
+     Text encoding        $xx
+     Language             $xx xx xx
+     Content descriptor   <text string according to encoding> $00 (00)
+     Lyrics/text          <full text string according to encoding>
+
+
+4.10.   Synchronised lyrics/text
+
+   This is another way of incorporating the words, said or sung lyrics,
+   in the audio file as text, this time, however, in sync with the
+   audio. It might also be used to describing events e.g. occurring on a
+   stage or on the screen in sync with the audio. The header includes a
+   content descriptor, represented with as terminated textstring. If no
+   descriptor is entered, 'Content descriptor' is $00 (00) only.
+
+     <Header for 'Synchronised lyrics/text', ID: "SYLT">
+     Text encoding        $xx
+     Language             $xx xx xx
+     Time stamp format    $xx
+     Content type         $xx
+     Content descriptor   <text string according to encoding> $00 (00)
+
+
+   Encoding:   $00  ISO-8859-1 [ISO-8859-1] character set is used => $00
+                    is sync identifier.
+               $01  Unicode [UNICODE] character set is used => $00 00 is
+                    sync identifier.
+
+   Content type:   $00 is other
+                   $01 is lyrics
+                   $02 is text transcription
+                   $03 is movement/part name (e.g. "Adagio")
+                   $04 is events (e.g. "Don Quijote enters the stage")
+                   $05 is chord (e.g. "Bb F Fsus")
+                   $06 is trivia/'pop up' information
+
+   Time stamp format is:
+
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Abolute time means that every stamp contains the time from the
+   beginning of the file.
+
+   The text that follows the frame header differs from that of the
+   unsynchronised lyrics/text transcription in one major way. Each
+   syllable (or whatever size of text is considered to be convenient by
+   the encoder) is a null terminated string followed by a time stamp
+   denoting where in the sound file it belongs. Each sync thus has the
+   following structure:
+
+     Terminated text to be synced (typically a syllable)
+     Sync identifier (terminator to above string)   $00 (00)
+     Time stamp                                     $xx (xx ...)
+
+   The 'time stamp' is set to zero or the whole sync is omitted if
+   located directly at the beginning of the sound. All time stamps
+   should be sorted in chronological order. The sync can be considered
+   as a validator of the subsequent string.
+
+   Newline ($0A) characters are allowed in all "SYLT" frames and should
+   be used after every entry (name, event etc.) in a frame with the
+   content type $03 - $04.
+
+   A few considerations regarding whitespace characters: Whitespace
+   separating words should mark the beginning of a new word, thus
+   occurring in front of the first syllable of a new word. This is also
+   valid for new line characters. A syllable followed by a comma should
+   not be broken apart with a sync (both the syllable and the comma
+   should be before the sync).
+
+   An example: The "USLT" passage
+
+     "Strangers in the night" $0A "Exchanging glances"
+
+   would be "SYLT" encoded as:
+
+     "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx
+     " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx
+     xx "glan" $00 xx xx "ces" $00 xx xx
+
+   There may be more than one "SYLT" frame in each tag, but only one
+   with the same language and content descriptor.
+
+
+4.11.   Comments
+
+   This frame is indended for any kind of full text information that
+   does not fit in any other frame. It consists of a frame header
+   followed by encoding, language and content descriptors and is ended
+   with the actual comment as a text string. Newline characters are
+   allowed in the comment text string. There may be more than one
+   comment frame in each tag, but only one with the same language and
+   content descriptor.
+
+     <Header for 'Comment', ID: "COMM">
+     Text encoding          $xx
+     Language               $xx xx xx
+     Short content descrip. <text string according to encoding> $00 (00)
+     The actual text        <full text string according to encoding>
+
+
+4.12.   Relative volume adjustment
+
+   This is a more subjective function than the previous ones. It allows
+   the user to say how much he wants to increase/decrease the volume on
+   each channel while the file is played. The purpose is to be able to
+   align all files to a reference volume, so that you don't have to
+   change the volume constantly. This frame may also be used to balance
+   adjust the audio. If the volume peak levels are known then this could
+   be described with the 'Peak volume right' and 'Peak volume left'
+   field. If Peakvolume is not known these fields could be left zeroed
+   or, if no other data follows, be completely omitted. There may only
+   be one "RVAD" frame in each tag.
+
+     <Header for 'Relative volume adjustment', ID: "RVAD">
+     Increment/decrement           %00xxxxxx
+     Bits used for volume descr.   $xx
+     Relative volume change, right $xx xx (xx ...)
+     Relative volume change, left  $xx xx (xx ...)
+     Peak volume right             $xx xx (xx ...)
+     Peak volume left              $xx xx (xx ...)
+
+   In the increment/decrement field bit 0 is used to indicate the right
+   channel and bit 1 is used to indicate the left channel. 1 is
+   increment and 0 is decrement.
+
+   The 'bits used for volume description' field is normally $10 (16
+   bits) for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value
+   may not be $00. The volume is always represented with whole bytes,
+   padded in the beginning (highest bits) when 'bits used for volume
+   description' is not a multiple of eight.
+
+   This datablock is then optionally followed by a volume definition for
+   the left and right back channels. If this information is appended to
+   the frame the first two channels will be treated as front channels.
+   In the increment/decrement field bit 2 is used to indicate the right
+   back channel and bit 3 for the left back channel.
+
+     Relative volume change, right back $xx xx (xx ...)
+     Relative volume change, left back  $xx xx (xx ...)
+     Peak volume right back             $xx xx (xx ...)
+     Peak volume left back              $xx xx (xx ...)
+
+   If the center channel adjustment is present the following is appended
+   to the existing frame, after the left and right back channels. The
+   center channel is represented by bit 4 in the increase/decrease
+   field.
+
+     Relative volume change, center  $xx xx (xx ...)
+     Peak volume center              $xx xx (xx ...)
+
+   If the bass channel adjustment is present the following is appended
+   to the existing frame, after the center channel. The bass channel is
+   represented by bit 5 in the increase/decrease field.
+
+     Relative volume change, bass  $xx xx (xx ...)
+     Peak volume bass              $xx xx (xx ...)
+
+
+4.13.   Equalisation
+
+   This is another subjective, alignment frame. It allows the user to
+   predefine an equalisation curve within the audio file. There may only
+   be one "EQUA" frame in each tag.
+
+     <Header of 'Equalisation', ID: "EQUA">
+     Adjustment bits    $xx
+
+   The 'adjustment bits' field defines the number of bits used for
+   representation of the adjustment. This is normally $10 (16 bits) for
+   MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be
+   $00.
+
+   This is followed by 2 bytes + ('adjustment bits' rounded up to the
+   nearest byte) for every equalisation band in the following format,
+   giving a frequency range of 0 - 32767Hz:
+
+     Increment/decrement   %x (MSB of the Frequency)
+     Frequency             (lower 15 bits)
+     Adjustment            $xx (xx ...)
+
+   The increment/decrement bit is 1 for increment and 0 for decrement.
+   The equalisation bands should be ordered increasingly with reference
+   to frequency. All frequencies don't have to be declared. The
+   equalisation curve in the reading software should be interpolated
+   between the values in this frame. Three equal adjustments for three
+   subsequent frequencies. A frequency should only be described once in
+   the frame.
+
+
+4.14.   Reverb
+
+   Yet another subjective one. You may here adjust echoes of different
+   kinds. Reverb left/right is the delay between every bounce in ms.
+   Reverb bounces left/right is the number of bounces that should be
+   made. $FF equals an infinite number of bounces. Feedback is the
+   amount of volume that should be returned to the next echo bounce. $00
+   is 0%, $FF is 100%. If this value were $7F, there would be 50% volume
+   reduction on the first bounce, 50% of that on the second and so on.
+   Left to left means the sound from the left bounce to be played in the
+   left speaker, while left to right means sound from the left bounce to
+   be played in the right speaker.
+
+   'Premix left to right' is the amount of left sound to be mixed in the
+   right before any reverb is applied, where $00 id 0% and $FF is 100%.
+   'Premix right to left' does the same thing, but right to left.
+   Setting both premix to $FF would result in a mono output (if the
+   reverb is applied symmetric). There may only be one "RVRB" frame in
+   each tag.
+
+     <Header for 'Reverb', ID: "RVRB">
+     Reverb left (ms)                 $xx xx
+     Reverb right (ms)                $xx xx
+     Reverb bounces, left             $xx
+     Reverb bounces, right            $xx
+     Reverb feedback, left to left    $xx
+     Reverb feedback, left to right   $xx
+     Reverb feedback, right to right  $xx
+     Reverb feedback, right to left   $xx
+     Premix left to right             $xx
+     Premix right to left             $xx
+
+
+4.15.   Attached picture
+
+   This frame contains a picture directly related to the audio file.
+   Image format is the MIME type and subtype [MIME] for the image. In
+   the event that the MIME media type name is omitted, "image/" will be
+   implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format
+   should be used when interoperability is wanted. Description is a
+   short description of the picture, represented as a terminated
+   textstring. The description has a maximum length of 64 characters,
+   but may be empty. There may be several pictures attached to one file,
+   each in their individual "APIC" frame, but only one with the same
+   content descriptor. There may only be one picture with the picture
+   type declared as picture type $01 and $02 respectively. There is the
+   possibility to put only a link to the image file by using the 'MIME
+   type' "-->" and having a complete URL [URL] instead of picture data.
+   The use of linked files should however be used sparingly since there
+   is the risk of separation of files.
+
+     <Header for 'Attached picture', ID: "APIC">
+     Text encoding      $xx
+     MIME type          <text string> $00
+     Picture type       $xx
+     Description        <text string according to encoding> $00 (00)
+     Picture data       <binary data>
+
+
+   Picture type:  $00  Other
+                  $01  32x32 pixels 'file icon' (PNG only)
+                  $02  Other file icon
+                  $03  Cover (front)
+                  $04  Cover (back)
+                  $05  Leaflet page
+                  $06  Media (e.g. lable side of CD)
+                  $07  Lead artist/lead performer/soloist
+                  $08  Artist/performer
+                  $09  Conductor
+                  $0A  Band/Orchestra
+                  $0B  Composer
+                  $0C  Lyricist/text writer
+                  $0D  Recording Location
+                  $0E  During recording
+                  $0F  During performance
+                  $10  Movie/video screen capture
+                  $11  A bright coloured fish
+                  $12  Illustration
+                  $13  Band/artist logotype
+                  $14  Publisher/Studio logotype
+
+
+4.16.   General encapsulated object
+
+   In this frame any type of file can be encapsulated. After the header,
+   'Frame size' and 'Encoding' follows 'MIME type' [MIME] represented as
+   as a terminated string encoded with ISO 8859-1 [ISO-8859-1]. The
+   filename is case sensitive and is encoded as 'Encoding'. Then follows
+   a content description as terminated string, encoded as 'Encoding'.
+   The last thing in the frame is the actual object. The first two
+   strings may be omitted, leaving only their terminations. MIME type is
+   always an ISO-8859-1 text string. There may be more than one "GEOB"
+   frame in each tag, but only one with the same content descriptor.
+
+     <Header for 'General encapsulated object', ID: "GEOB">
+     Text encoding          $xx
+     MIME type              <text string> $00
+     Filename               <text string according to encoding> $00 (00)
+     Content description    <text string according to encóding> $00 (00)
+     Encapsulated object    <binary data>
+
+
+4.17.   Play counter
+
+   This is simply a counter of the number of times a file has been
+   played. The value is increased by one every time the file begins to
+   play. There may only be one "PCNT" frame in each tag. When the
+   counter reaches all one's, one byte is inserted in front of the
+   counter thus making the counter eight bits bigger.  The counter must
+   be at least 32-bits long to begin with.
+
+     <Header for 'Play counter', ID: "PCNT">
+     Counter        $xx xx xx xx (xx ...)
+
+
+4.18.   Popularimeter
+
+   The purpose of this frame is to specify how good an audio file is.
+   Many interesting applications could be found to this frame such as a
+   playlist that features better audiofiles more often than others or it
+   could be used to profile a person's taste and find other 'good' files
+   by comparing people's profiles. The frame is very simple. It contains
+   the email address to the user, one rating byte and a four byte play
+   counter, intended to be increased with one for every time the file is
+   played. The email is a terminated string. The rating is 1-255 where
+   1 is worst and 255 is best. 0 is unknown. If no personal counter is
+   wanted it may be omitted.  When the counter reaches all one's, one
+   byte is inserted in front of the counter thus making the counter
+   eight bits bigger in the same away as the play counter ("PCNT").
+   There may be more than one "POPM" frame in each tag, but only one
+   with the same email address.
+
+     <Header for 'Popularimeter', ID: "POPM">
+     Email to user   <text string> $00
+     Rating          $xx
+     Counter         $xx xx xx xx (xx ...)
+
+
+4.19.   Recommended buffer size
+
+   Sometimes the server from which a audio file is streamed is aware of
+   transmission or coding problems resulting in interruptions in the
+   audio stream. In these cases, the size of the buffer can be
+   recommended by the server using this frame. If the 'embedded info
+   flag' is true (1) then this indicates that an ID3 tag with the
+   maximum size described in 'Buffer size' may occur in the audiostream.
+   In such case the tag should reside between two MPEG [MPEG] frames, if
+   the audio is MPEG encoded. If the position of the next tag is known,
+   'offset to next tag' may be used. The offset is calculated from the
+   end of tag in which this frame resides to the first byte of the
+   header in the next. This field may be omitted. Embedded tags are
+   generally not recommended since this could render unpredictable
+   behaviour from present software/hardware.
+
+   For applications like streaming audio it might be an idea to embed
+   tags into the audio stream though. If the clients connects to
+   individual connections like HTTP and there is a possibility to begin
+   every transmission with a tag, then this tag should include a
+   'recommended buffer size' frame. If the client is connected to a
+   arbitrary point in the stream, such as radio or multicast, then the
+   'recommended buffer size' frame should be included in every tag.
+   Every tag that is picked up after the initial/first tag is to be
+   considered as an update of the previous one. E.g. if there is a
+   "TIT2" frame in the first received tag and one in the second tag,
+   then the first should be 'replaced' with the second.
+
+   The 'Buffer size' should be kept to a minimum. There may only be one
+   "RBUF" frame in each tag.
+
+     <Header for 'Recommended buffer size', ID: "RBUF">
+     Buffer size               $xx xx xx
+     Embedded info flag        %0000000x
+     Offset to next tag        $xx xx xx xx
+
+
+4.20.   Audio encryption
+
+   This frame indicates if the actual audio stream is encrypted, and by
+   whom. Since standardisation of such encrypion scheme is beyond this
+   document, all "AENC" frames begin with a terminated string with a
+   URL containing an email address, or a link to a location where an
+   email address can be found, that belongs to the organisation
+   responsible for this specific encrypted audio file. Questions
+   regarding the encrypted audio should be sent to the email address
+   specified. If a $00 is found directly after the 'Frame size' and the
+   audiofile indeed is encrypted, the whole file may be considered
+   useless.
+
+   After the 'Owner identifier', a pointer to an unencrypted part of the
+   audio can be specified. The 'Preview start' and 'Preview length' is
+   described in frames. If no part is unencrypted, these fields should
+   be left zeroed. After the 'preview length' field follows optionally a
+   datablock required for decryption of the audio. There may be more
+   than one "AENC" frames in a tag, but only one with the same 'Owner
+   identifier'.
+
+     <Header for 'Audio encryption', ID: "AENC">
+     Owner identifier   <text string> $00
+     Preview start      $xx xx
+     Preview length     $xx xx
+     Encryption info    <binary data>
+
+
+4.21.   Linked information
+
+   To keep space waste as low as possible this frame may be used to link
+   information from another ID3v2 tag that might reside in another audio
+   file or alone in a binary file. It is recommended that this method is
+   only used when the files are stored on a CD-ROM or other
+   circumstances when the risk of file seperation is low. The frame
+   contains a frame identifier, which is the frame that should be linked
+   into this tag, a URL [URL] field, where a reference to the file where
+   the frame is given, and additional ID data, if needed. Data should be
+   retrieved from the first tag found in the file to which this link
+   points. There may be more than one "LINK" frame in a tag, but only
+   one with the same contents. A linked frame is to be considered as
+   part of the tag and has the same restrictions as if it was a physical
+   part of the tag (i.e. only one "RVRB" frame allowed, whether it's
+   linked or not).
+
+     <Header for 'Linked information', ID: "LINK">
+     Frame identifier        $xx xx xx
+     URL                     <text string> $00
+     ID and additional data  <text string(s)>
+
+   Frames that may be linked and need no additional data are "IPLS",
+   "MCID", "ETCO", "MLLT", "SYTC", "RVAD", "EQUA", "RVRB", "RBUF", the
+   text information frames and the URL link frames.
+
+   The "TXXX", "APIC", "GEOB" and "AENC" frames may be linked with
+   the content descriptor as additional ID data.
+
+   The "COMM", "SYLT" and "USLT" frames may be linked with three bytes
+   of language descriptor directly followed by a content descriptor as
+   additional ID data.
+
+
+4.22.   Position synchronisation frame
+
+   This frame delivers information to the listener of how far into the
+   audio stream he picked up; in effect, it states the time offset of
+   the first frame in the stream. The frame layout is:
+
+     <Head for 'Position synchronisation', ID: "POSS">
+     Time stamp format         $xx
+     Position                  $xx (xx ...)
+
+   Where time stamp format is:
+
+     $01  Absolute time, 32 bit sized, using MPEG frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   and position is where in the audio the listener starts to receive,
+   i.e. the beginning of the next frame. If this frame is used in the
+   beginning of a file the value is always 0. There may only be one
+   "POSS" frame in each tag.
+
+
+4.23.   Terms of use frame
+
+   This frame contains a brief description of the terms of use and
+   ownership of the file. More detailed information concerning the legal
+   terms might be available through the "WCOP" frame. Newlines are
+   allowed in the text. There may only be one "USER" frame in a tag.
+
+     <Header for 'Terms of use frame', ID: "USER">
+     Text encoding        $xx
+     Language             $xx xx xx
+     The actual text      <text string according to encoding>
+
+
+4.24.   Ownership frame
+
+   The ownership frame might be used as a reminder of a made transaction
+   or, if signed, as proof. Note that the "USER" and "TOWN" frames are
+   good to use in conjunction with this one. The frame begins, after the
+   frame ID, size and encoding fields, with a 'price payed' field. The
+   first three characters of this field contains the currency used for
+   the transaction, encoded according to ISO 4217 [ISO-4217] alphabetic
+   currency code. Concatenated to this is the actual price payed, as a
+   numerical string using "." as the decimal separator. Next is an 8
+   character date string (YYYYMMDD) followed by a string with the name
+   of the seller as the last field in the frame. There may only be one
+   "OWNE" frame in a tag.
+
+     <Header for 'Ownership frame', ID: "OWNE">
+     Text encoding     $xx
+     Price payed       <text string> $00
+     Date of purch.    <text string>
+     Seller            <text string according to encoding>
+
+
+4.25.   Commercial frame
+
+   This frame enables several competing offers in the same tag by
+   bundling all needed information. That makes this frame rather complex
+   but it's an easier solution than if one tries to achieve the same
+   result with several frames. The frame begins, after the frame ID,
+   size and encoding fields, with a price string field. A price is
+   constructed by one three character currency code, encoded according
+   to ISO 4217 [ISO-4217] alphabetic currency code, followed by a
+   numerical value where "." is used as decimal seperator. In the price
+   string several prices may be concatenated, seperated by a "/"
+   character, but there may only be one currency of each type.
+
+   The price string is followed by an 8 character date string in the
+   format YYYYMMDD, describing for how long the price is valid. After
+   that is a contact URL, with which the user can contact the seller,
+   followed by a one byte 'received as' field. It describes how the
+   audio is delivered when bought according to the following list:
+
+        $00  Other
+        $01  Standard CD album with other songs
+        $02  Compressed audio on CD
+        $03  File over the Internet
+        $04  Stream over the Internet
+        $05  As note sheets
+        $06  As note sheets in a book with other sheets
+        $07  Music on other media
+        $08  Non-musical merchandise
+
+   Next follows a terminated string with the name of the seller followed
+   by a terminated string with a short description of the product. The
+   last thing is the ability to include a company logotype. The first of
+   them is the 'Picture MIME type' field containing information about
+   which picture format is used. In the event that the MIME media type
+   name is omitted, "image/" will be implied. Currently only "image/png"
+   and "image/jpeg" are allowed. This format string is followed by the
+   binary picture data. This two last fields may be omitted if no
+   picture is to attach.
+
+     <Header for 'Commercial frame', ID: "COMR">
+     Text encoding      $xx
+     Price string       <text string> $00
+     Valid until        <text string>
+     Contact URL        <text string> $00
+     Received as        $xx
+     Name of seller     <text string according to encoding> $00 (00)
+     Description        <text string according to encoding> $00 (00)
+     Picture MIME type  <string> $00
+     Seller logo        <binary data>
+
+
+4.26.   Encryption method registration
+
+   To identify with which method a frame has been encrypted the
+   encryption method must be registered in the tag with this frame. The
+   'Owner identifier' is a null-terminated string with a URL [URL]
+   containing an email address, or a link to a location where an email
+   address can be found, that belongs to the organisation responsible
+   for this specific encryption method. Questions regarding the
+   encryption method should be sent to the indicated email address. The
+   'Method symbol' contains a value that is associated with this method
+   throughout the whole tag. Values below $80 are reserved. The 'Method
+   symbol' may optionally be followed by encryption specific data. There
+   may be several "ENCR" frames in a tag but only one containing the
+   same symbol and only one containing the same owner identifier. The
+   method must be used somewhere in the tag. See section 3.3.1, flag j
+   for more information.
+
+     <Header for 'Encryption method registration', ID: "ENCR">
+     Owner identifier    <text string> $00
+     Method symbol       $xx
+     Encryption data     <binary data>
+
+
+4.27.   Group identification registration
+
+   This frame enables grouping of otherwise unrelated frames. This can
+   be used when some frames are to be signed. To identify which frames
+   belongs to a set of frames a group identifier must be registered in
+   the tag with this frame. The 'Owner identifier' is a null-terminated
+   string with a URL [URL] containing an email address, or a link to a
+   location where an email address can be found, that belongs to the
+   organisation responsible for this grouping. Questions regarding the
+   grouping should be sent to the indicated email address. The 'Group
+   symbol' contains a value that associates the frame with this group
+   throughout the whole tag. Values below $80 are reserved. The 'Group
+   symbol' may optionally be followed by some group specific data, e.g.
+   a digital signature. There may be several "GRID" frames in a tag but
+   only one containing the same symbol and only one containing the same
+   owner identifier. The group symbol must be used somewhere in the tag.
+   See section 3.3.1, flag j for more information.
+
+     <Header for 'Group ID registration', ID: "GRID">
+     Owner identifier      <text string> $00
+     Group symbol          $xx
+        Group dependent data  <binary data>
+
+
+4.28.   Private frame
+
+   This frame is used to contain information from a software producer
+   that its program uses and does not fit into the other frames. The
+   frame consists of an 'Owner identifier' string and the binary data.
+   The 'Owner identifier' is a null-terminated string with a URL [URL]
+   containing an email address, or a link to a location where an email
+   address can be found, that belongs to the organisation responsible
+   for the frame. Questions regarding the frame should be sent to the
+   indicated email address. The tag may contain more than one "PRIV"
+   frame but only with different contents. It is recommended to keep the
+   number of "PRIV" frames as low as possible.
+
+     <Header for 'Private frame', ID: "PRIV">
+     Owner identifier      <text string> $00
+        The private data      <binary data>
+
+
+5.   The 'unsynchronisation scheme'
+
+   The only purpose of the 'unsynchronisation scheme' is to make the
+   ID3v2 tag as compatible as possible with existing software. There is
+   no use in 'unsynchronising' tags if the file is only to be processed
+   by new software. Unsynchronisation may only be made with MPEG 2 layer
+   I, II and III and MPEG 2.5 files.
+
+   Whenever a false synchronisation is found within the tag, one zeroed
+   byte is inserted after the first false synchronisation byte. The
+   format of a correct sync that should be altered by ID3 encoders is as
+   follows:
+
+         %11111111 111xxxxx
+
+   And should be replaced with:
+
+         %11111111 00000000 111xxxxx
+
+   This has the side effect that all $FF 00 combinations have to be
+   altered, so they won't be affected by the decoding process. Therefore
+   all the $FF 00 combinations have to be replaced with the $FF 00 00
+   combination during the unsynchronisation.
+
+   To indicate usage of the unsynchronisation, the first bit in 'ID3
+   flags' should be set. This bit should only be set if the tag
+   contains a, now corrected, false synchronisation. The bit should
+   only be clear if the tag does not contain any false synchronisations.
+
+   Do bear in mind, that if a compression scheme is used by the encoder,
+   the unsynchronisation scheme should be applied *afterwards*. When
+   decoding a compressed, 'unsynchronised' file, the 'unsynchronisation
+   scheme' should be parsed first, decompression afterwards.
+
+   If the last byte in the tag is $FF, and there is a need to eliminate
+   false synchronisations in the tag, at least one byte of padding
+   should be added.
+
+
+6.   Copyright
+
+   Copyright (C) Martin Nilsson 1998. All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that a reference to this document is included on all
+   such copies and derivative works. However, this document itself may
+   not be modified in any way and reissued as the original document.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+   THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+   WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+7.   References
+
+   [CDDB] Compact Disc Data Base
+
+      http://www.cddb.com
+
+   [ID3v2] Martin Nilsson, "ID3v2 informal standard".
+
+      http://www.id3lib.org/id3/id3v2-00.txt
+
+   [ISO-639-2] ISO/FDIS 639-2.
+   Codes for the representation of names of languages, Part 2: Alpha-3
+   code. Technical committee / subcommittee: TC 37 / SC 2
+
+   [ISO-4217] ISO 4217:1995.
+   Codes for the representation of currencies and funds.
+   Technical committee / subcommittee: TC 68
+
+   [ISO-8859-1] ISO/IEC DIS 8859-1.
+   8-bit single-byte coded graphic character sets, Part 1: Latin
+   alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2
+
+   [ISRC] ISO 3901:1986
+   International Standard Recording Code (ISRC).
+   Technical committee / subcommittee: TC 46 / SC 9
+
+   [JFIF] JPEG File Interchange Format, version 1.02
+
+      http://www.w3.org/Graphics/JPEG/jfif.txt">http://www.w3.org/Graphics/JPEG/jfif.txt
+
+   [MIME] Freed, N.  and N. Borenstein,  "Multipurpose Internet Mail
+   Extensions (MIME) Part One: Format of Internet Message Bodies",
+   RFC 2045, November 1996.
+
+      ftp://ftp.isi.edu/in-notes/rfc2045.txt">ftp://ftp.isi.edu/in-notes/rfc2045.txt
+
+   [MPEG] ISO/IEC 11172-3:1993.
+   Coding of moving pictures and associated audio for digital storage
+   media at up to about 1,5 Mbit/s, Part 3: Audio.
+   Technical committee / subcommittee: JTC 1 / SC 29
+    and
+   ISO/IEC 13818-3:1995
+   Generic coding of moving pictures and associated audio information,
+   Part 3: Audio.
+   Technical committee / subcommittee: JTC 1 / SC 29
+    and
+   ISO/IEC DIS 13818-3
+   Generic coding of moving pictures and associated audio information,
+   Part 3: Audio (Revision of ISO/IEC 13818-3:1995)
+
+
+   [PNG] Portable Network Graphics, version 1.0
+
+      http://www.w3.org/TR/REC-png-multi.html
+
+   [UNICODE] ISO/IEC 10646-1:1993.
+   Universal Multiple-Octet Coded Character Set (UCS), Part 1:
+   Architecture and Basic Multilingual Plane.
+   Technical committee / subcommittee: JTC 1 / SC 2
+
+      http://www.unicode.org/
+
+   [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource
+   Locators (URL).", RFC 1738, December 1994.
+
+      ftp://ftp.isi.edu/in-notes/rfc1738.txt
+
+   [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, "ZLIB
+   Compressed
+   Data Format Specification version 3.3", RFC 1950, May 1996.
+
+      ftp://ftp.isi.edu/in-notes/rfc1950.txt
+
+
+8.   Appendix
+
+
+A.   Appendix A - Genre List from ID3v1
+
+   The following genres is defined in ID3v1
+
+      0.Blues
+      1.Classic Rock
+      2.Country
+      3.Dance
+      4.Disco
+      5.Funk
+      6.Grunge
+      7.Hip-Hop
+      8.Jazz
+      9.Metal
+     10.New Age
+     11.Oldies
+     12.Other
+     13.Pop
+     14.R&B
+     15.Rap
+     16.Reggae
+     17.Rock
+     18.Techno
+     19.Industrial
+     20.Alternative
+     21.Ska
+     22.Death Metal
+     23.Pranks
+     24.Soundtrack
+     25.Euro-Techno
+     26.Ambient
+     27.Trip-Hop
+     28.Vocal
+     29.Jazz+Funk
+     30.Fusion
+     31.Trance
+     32.Classical
+     33.Instrumental
+     34.Acid
+     35.House
+     36.Game
+     37.Sound Clip
+     38.Gospel
+     39.Noise
+     40.AlternRock
+     41.Bass
+     42.Soul
+     43.Punk
+     44.Space
+     45.Meditative
+     46.Instrumental Pop
+     47.Instrumental Rock
+     48.Ethnic
+     49.Gothic
+     50.Darkwave
+     51.Techno-Industrial
+     52.Electronic
+     53.Pop-Folk
+     54.Eurodance
+     55.Dream
+     56.Southern Rock
+     57.Comedy
+     58.Cult
+     59.Gangsta
+     60.Top 40
+     61.Christian Rap
+     62.Pop/Funk
+     63.Jungle
+     64.Native American
+     65.Cabaret
+     66.New Wave
+     67.Psychadelic
+     68.Rave
+     69.Showtunes
+     70.Trailer
+     71.Lo-Fi
+     72.Tribal
+     73.Acid Punk
+     74.Acid Jazz
+     75.Polka
+     76.Retro
+     77.Musical
+     78.Rock & Roll
+     79.Hard Rock
+
+   The following genres are Winamp extensions
+
+     80.Folk
+     81.Folk-Rock
+     82.National Folk
+     83.Swing
+     84.Fast Fusion
+     85.Bebob
+     86.Latin
+     87.Revival
+     88.Celtic
+     89.Bluegrass
+     90.Avantgarde
+     91.Gothic Rock
+     92.Progressive Rock
+     93.Psychedelic Rock
+     94.Symphonic Rock
+     95.Slow Rock
+     96.Big Band
+     97.Chorus
+     98.Easy Listening
+     99.Acoustic
+    100.Humour
+    101.Speech
+    102.Chanson
+    103.Opera
+    104.Chamber Music
+    105.Sonata
+    106.Symphony
+    107.Booty Bass
+    108.Primus
+    109.Porn Groove
+    110.Satire
+    111.Slow Jam
+    112.Club
+    113.Tango
+    114.Samba
+    115.Folklore
+    116.Ballad
+    117.Power Ballad
+    118.Rhythmic Soul
+    119.Freestyle
+    120.Duet
+    121.Punk Rock
+    122.Drum Solo
+    123.Acapella
+    124.Euro-House
+    125.Dance Hall
+
+
+9.   Author's Address
+
+   Written by
+
+     Martin Nilsson
+     Rydsvägen 246 C. 30
+     S-584 34 Linköping
+     Sweden
+
+     Email: nilsson@id3.org
+
+
+   Edited by
+
+     Dirk Mahoney
+     57 Pechey Street
+     Chermside Q
+     Australia 4032
+
+     Email: dirk@id3.org
+
+
+     Johan Sundström
+     Alsättersgatan 5 A. 34
+     S-584 35 Linköping
+     Sweden
+
+     Email: johan@id3.org
diff --git a/src/taglib/mpeg/id3v2/id3v2.4.0-frames.txt b/src/taglib/mpeg/id3v2/id3v2.4.0-frames.txt
new file mode 100644 (file)
index 0000000..457acaa
--- /dev/null
@@ -0,0 +1,1734 @@
+$Id: id3v2.4.0-frames.txt 168944 2002-07-26 20:59:59Z wheeler $
+
+Informal standard                                             M. Nilsson
+Document: id3v2.4.0-frames.txt                         1st November 2000
+
+
+                  ID3 tag version 2.4.0 - Native Frames
+
+Status of this document
+
+   This document is an informal standard and replaces the ID3v2.3.0
+   standard [ID3v2]. A formal standard will use another revision number
+   even if the content is identical to document. The contents in this
+   document may change for clarifications but never for added or altered
+   functionallity.
+
+   Distribution of this document is unlimited.
+
+
+Abstract
+
+   This document describes the frames natively supported by ID3v2.4.0,
+   which is a revised version of the ID3v2 informal standard [ID3v2.3.0]
+   version 2.3.0. The ID3v2 offers a flexible way of storing audio meta
+   information within audio file itself. The information may be
+   technical information, such as equalisation curves, as well as title,
+   performer, copyright etc.
+
+   ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order
+   to allow for implementations to be revised as easily as possible.
+
+
+1.   Table of contents
+
+   2.   Conventions in this document
+   3.   Default flags
+   4.   Declared ID3v2 frames
+     4.1.   Unique file identifier
+     4.2.   Text information frames
+       4.2.1.   Identification frames
+       4.2.2.   Involved persons frames
+       4.2.3.   Derived and subjective properties frames
+       4.2.4.   Rights and license frames
+       4.2.5.   Other text frames
+       4.2.6.   User defined text information frame
+     4.3.   URL link frames
+       4.3.1.   URL link frames - details
+       4.3.2.   User defined URL link frame
+     4.4.   Music CD Identifier
+     4.5.   Event timing codes
+     4.6.   MPEG location lookup table
+     4.7.   Synced tempo codes
+     4.8.   Unsynchronised lyrics/text transcription
+     4.9.  Synchronised lyrics/text
+     4.10.  Comments
+     4.11.  Relative volume adjustment (2)
+     4.12.  Equalisation (2)
+     4.13.  Reverb
+     4.14.  Attached picture
+     4.15.  General encapsulated object
+     4.16.  Play counter
+     4.17.  Popularimeter
+     4.18.  Recommended buffer size
+     4.19.  Audio encryption
+     4.20.  Linked information
+     4.21.  Position synchronisation frame
+     4.22.  Terms of use
+     4.23.  Ownership frame
+     4.24.  Commercial frame
+     4.25.  Encryption method registration
+     4.26.  Group identification registration
+     4.27.  Private frame
+     4.28.  Signature frame
+     4.29.  Seek frame
+     4.30.  Audio seek point index
+   5.   Copyright
+   6.   References
+   7.   Appendix
+     A.   Appendix A - Genre List from ID3v1
+   8.   Author's Address
+
+
+2.   Conventions in this document
+
+   Text within "" is a text string exactly as it appears in a tag.
+   Numbers preceded with $ are hexadecimal and numbers preceded with %
+   are binary. $xx is used to indicate a byte with unknown content. %x
+   is used to indicate a bit with unknown content. The most significant
+   bit (MSB) of a byte is called 'bit 7' and the least significant bit
+   (LSB) is called 'bit 0'.
+
+   A tag is the whole tag described the ID3v2 main structure document
+   [ID3v2-strct]. A frame is a block of information in the tag. The tag
+   consists of a header, frames and optional padding. A field is a piece
+   of information; one value, a string etc. A numeric string is a string
+   that consists of the characters "0123456789" only.
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED",  "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+
+3.   Default flags
+
+   The default settings for the frames described in this document can be
+   divided into the following classes. The flags may be set differently
+   if found more suitable by the software.
+
+    1. Discarded if tag is altered, discarded if file is altered.
+
+       None.
+
+    2. Discarded if tag is altered, preserved if file is altered.
+
+       None.
+
+    3. Preserved if tag is altered, discarded if file is altered.
+
+       ASPI, AENC, ETCO, EQU2, MLLT, POSS, SEEK, SYLT, SYTC, RVA2, TENC,
+       TLEN
+
+    4. Preserved if tag is altered, preserved if file is altered.
+
+       The rest of the frames.
+
+
+4.   Declared ID3v2 frames
+
+   The following frames are declared in this draft.
+
+  4.19  AENC Audio encryption
+  4.14  APIC Attached picture
+  4.30  ASPI Audio seek point index
+
+  4.10  COMM Comments
+  4.24  COMR Commercial frame
+
+  4.25  ENCR Encryption method registration
+  4.12  EQU2 Equalisation (2)
+  4.5   ETCO Event timing codes
+
+  4.15  GEOB General encapsulated object
+  4.26  GRID Group identification registration
+
+  4.20  LINK Linked information
+
+  4.4   MCDI Music CD identifier
+  4.6   MLLT MPEG location lookup table
+
+  4.23  OWNE Ownership frame
+
+  4.27  PRIV Private frame
+  4.16  PCNT Play counter
+  4.17  POPM Popularimeter
+  4.21  POSS Position synchronisation frame
+
+  4.18  RBUF Recommended buffer size
+  4.11  RVA2 Relative volume adjustment (2)
+  4.13  RVRB Reverb
+
+  4.29  SEEK Seek frame
+  4.28  SIGN Signature frame
+  4.9   SYLT Synchronised lyric/text
+  4.7   SYTC Synchronised tempo codes
+
+  4.2.1 TALB Album/Movie/Show title
+  4.2.3 TBPM BPM (beats per minute)
+  4.2.2 TCOM Composer
+  4.2.3 TCON Content type
+  4.2.4 TCOP Copyright message
+  4.2.5 TDEN Encoding time
+  4.2.5 TDLY Playlist delay
+  4.2.5 TDOR Original release time
+  4.2.5 TDRC Recording time
+  4.2.5 TDRL Release time
+  4.2.5 TDTG Tagging time
+  4.2.2 TENC Encoded by
+  4.2.2 TEXT Lyricist/Text writer
+  4.2.3 TFLT File type
+  4.2.2 TIPL Involved people list
+  4.2.1 TIT1 Content group description
+  4.2.1 TIT2 Title/songname/content description
+  4.2.1 TIT3 Subtitle/Description refinement
+  4.2.3 TKEY Initial key
+  4.2.3 TLAN Language(s)
+  4.2.3 TLEN Length
+  4.2.2 TMCL Musician credits list
+  4.2.3 TMED Media type
+  4.2.3 TMOO Mood
+  4.2.1 TOAL Original album/movie/show title
+  4.2.5 TOFN Original filename
+  4.2.2 TOLY Original lyricist(s)/text writer(s)
+  4.2.2 TOPE Original artist(s)/performer(s)
+  4.2.4 TOWN File owner/licensee
+  4.2.2 TPE1 Lead performer(s)/Soloist(s)
+  4.2.2 TPE2 Band/orchestra/accompaniment
+  4.2.2 TPE3 Conductor/performer refinement
+  4.2.2 TPE4 Interpreted, remixed, or otherwise modified by
+  4.2.1 TPOS Part of a set
+  4.2.4 TPRO Produced notice
+  4.2.4 TPUB Publisher
+  4.2.1 TRCK Track number/Position in set
+  4.2.4 TRSN Internet radio station name
+  4.2.4 TRSO Internet radio station owner
+  4.2.5 TSOA Album sort order
+  4.2.5 TSOP Performer sort order
+  4.2.5 TSOT Title sort order
+  4.2.1 TSRC ISRC (international standard recording code)
+  4.2.5 TSSE Software/Hardware and settings used for encoding
+  4.2.1 TSST Set subtitle
+  4.2.2 TXXX User defined text information frame
+
+  4.1   UFID Unique file identifier
+  4.22  USER Terms of use
+  4.8   USLT Unsynchronised lyric/text transcription
+
+  4.3.1 WCOM Commercial information
+  4.3.1 WCOP Copyright/Legal information
+  4.3.1 WOAF Official audio file webpage
+  4.3.1 WOAR Official artist/performer webpage
+  4.3.1 WOAS Official audio source webpage
+  4.3.1 WORS Official Internet radio station homepage
+  4.3.1 WPAY Payment
+  4.3.1 WPUB Publishers official webpage
+  4.3.2 WXXX User defined URL link frame
+
+
+4.1.   Unique file identifier
+
+   This frame's purpose is to be able to identify the audio file in a
+   database, that may provide more information relevant to the content.
+   Since standardisation of such a database is beyond this document, all
+   UFID frames begin with an 'owner identifier' field. It is a null-
+   terminated string with a URL [URL] containing an email address, or a
+   link to a location where an email address can be found, that belongs
+   to the organisation responsible for this specific database
+   implementation. Questions regarding the database should be sent to
+   the indicated email address. The URL should not be used for the
+   actual database queries. The string
+   "http://www.id3.org/dummy/ufid.html" should be used for tests. The
+   'Owner identifier' must be non-empty (more than just a termination).
+   The 'Owner identifier' is then followed by the actual identifier,
+   which may be up to 64 bytes. There may be more than one "UFID" frame
+   in a tag, but only one with the same 'Owner identifier'.
+
+     <Header for 'Unique file identifier', ID: "UFID">
+     Owner identifier        <text string> $00
+     Identifier              <up to 64 bytes binary data>
+
+
+4.2.   Text information frames
+
+   The text information frames are often the most important frames,
+   containing information like artist, album and more. There may only be
+   one text information frame of its kind in an tag. All text
+   information frames supports multiple strings, stored as a null
+   separated list, where null is reperesented by the termination code
+   for the charater encoding. All text frame identifiers begin with "T".
+   Only text frame identifiers begin with "T", with the exception of the
+   "TXXX" frame. All the text information frames have the following
+   format:
+
+     <Header for 'Text information frame', ID: "T000" - "TZZZ",
+     excluding "TXXX" described in 4.2.6.>
+     Text encoding                $xx
+     Information                  <text string(s) according to encoding>
+
+
+4.2.1.   Identification frames
+
+  TIT1
+   The 'Content group description' frame is used if the sound belongs to
+   a larger category of sounds/music. For example, classical music is
+   often sorted in different musical sections (e.g. "Piano Concerto",
+   "Weather - Hurricane").
+
+  TIT2
+   The 'Title/Songname/Content description' frame is the actual name of
+   the piece (e.g. "Adagio", "Hurricane Donna").
+
+  TIT3
+   The 'Subtitle/Description refinement' frame is used for information
+   directly related to the contents title (e.g. "Op. 16" or "Performed
+   live at Wembley").
+
+  TALB
+   The 'Album/Movie/Show title' frame is intended for the title of the
+   recording (or source of sound) from which the audio in the file is
+   taken.
+
+  TOAL
+   The 'Original album/movie/show title' frame is intended for the title
+   of the original recording (or source of sound), if for example the
+   music in the file should be a cover of a previously released song.
+
+  TRCK
+   The 'Track number/Position in set' frame is a numeric string
+   containing the order number of the audio-file on its original
+   recording. This MAY be extended with a "/" character and a numeric
+   string containing the total number of tracks/elements on the original
+   recording. E.g. "4/9".
+
+  TPOS
+   The 'Part of a set' frame is a numeric string that describes which
+   part of a set the audio came from. This frame is used if the source
+   described in the "TALB" frame is divided into several mediums, e.g. a
+   double CD. The value MAY be extended with a "/" character and a
+   numeric string containing the total number of parts in the set. E.g.
+   "1/2".
+
+  TSST
+   The 'Set subtitle' frame is intended for the subtitle of the part of
+   a set this track belongs to.
+
+  TSRC
+   The 'ISRC' frame should contain the International Standard Recording
+   Code [ISRC] (12 characters).
+
+
+4.2.2.   Involved persons frames
+
+  TPE1
+   The 'Lead artist/Lead performer/Soloist/Performing group' is
+   used for the main artist.
+
+  TPE2
+   The 'Band/Orchestra/Accompaniment' frame is used for additional
+   information about the performers in the recording.
+
+  TPE3
+   The 'Conductor' frame is used for the name of the conductor.
+
+  TPE4
+   The 'Interpreted, remixed, or otherwise modified by' frame contains
+   more information about the people behind a remix and similar
+   interpretations of another existing piece.
+
+  TOPE
+   The 'Original artist/performer' frame is intended for the performer
+   of the original recording, if for example the music in the file
+   should be a cover of a previously released song.
+
+  TEXT
+   The 'Lyricist/Text writer' frame is intended for the writer of the
+   text or lyrics in the recording.
+
+  TOLY
+   The 'Original lyricist/text writer' frame is intended for the
+   text writer of the original recording, if for example the music in
+   the file should be a cover of a previously released song.
+
+  TCOM
+   The 'Composer' frame is intended for the name of the composer.
+
+  TMCL
+   The 'Musician credits list' is intended as a mapping between
+   instruments and the musician that played it. Every odd field is an
+   instrument and every even is an artist or a comma delimited list of
+   artists.
+
+  TIPL
+   The 'Involved people list' is very similar to the musician credits
+   list, but maps between functions, like producer, and names.
+
+  TENC
+   The 'Encoded by' frame contains the name of the person or
+   organisation that encoded the audio file. This field may contain a
+   copyright message, if the audio file also is copyrighted by the
+   encoder.
+
+
+4.2.3.   Derived and subjective properties frames
+
+  TBPM
+   The 'BPM' frame contains the number of beats per minute in the
+   main part of the audio. The BPM is an integer and represented as a
+   numerical string.
+
+  TLEN
+   The 'Length' frame contains the length of the audio file in
+   milliseconds, represented as a numeric string.
+
+  TKEY
+   The 'Initial key' frame contains the musical key in which the sound
+   starts. It is represented as a string with a maximum length of three
+   characters. The ground keys are represented with "A","B","C","D","E",
+   "F" and "G" and halfkeys represented with "b" and "#". Minor is
+   represented as "m", e.g. "Dbm" $00. Off key is represented with an
+   "o" only.
+
+  TLAN
+   The 'Language' frame should contain the languages of the text or
+   lyrics spoken or sung in the audio. The language is represented with
+   three characters according to ISO-639-2 [ISO-639-2]. If more than one
+   language is used in the text their language codes should follow
+   according to the amount of their usage, e.g. "eng" $00 "sve" $00.
+
+  TCON
+   The 'Content type', which ID3v1 was stored as a one byte numeric
+   value only, is now a string. You may use one or several of the ID3v1
+   types as numerical strings, or, since the category list would be
+   impossible to maintain with accurate and up to date categories,
+   define your own. Example: "21" $00 "Eurodisco" $00
+
+   You may also use any of the following keywords:
+   
+     RX  Remix
+     CR  Cover
+
+  TFLT
+   The 'File type' frame indicates which type of audio this tag defines.
+   The following types and refinements are defined:
+
+     MIME   MIME type follows
+     MPG    MPEG Audio
+       /1     MPEG 1/2 layer I
+       /2     MPEG 1/2 layer II
+       /3     MPEG 1/2 layer III
+       /2.5   MPEG 2.5
+       /AAC   Advanced audio compression
+     VQF    Transform-domain Weighted Interleave Vector Quantisation
+     PCM    Pulse Code Modulated audio
+
+   but other types may be used, but not for these types though. This is
+   used in a similar way to the predefined types in the "TMED" frame,
+   but without parentheses. If this frame is not present audio type is
+   assumed to be "MPG".
+
+  TMED
+   The 'Media type' frame describes from which media the sound
+   originated. This may be a text string or a reference to the
+   predefined media types found in the list below. Example:
+   "VID/PAL/VHS" $00.
+
+    DIG    Other digital media
+      /A    Analogue transfer from media
+
+    ANA    Other analogue media
+      /WAC  Wax cylinder
+      /8CA  8-track tape cassette
+
+    CD     CD
+      /A    Analogue transfer from media
+      /DD   DDD
+      /AD   ADD
+      /AA   AAD
+
+    LD     Laserdisc
+
+    TT     Turntable records
+      /33    33.33 rpm
+      /45    45 rpm
+      /71    71.29 rpm
+      /76    76.59 rpm
+      /78    78.26 rpm
+      /80    80 rpm
+
+    MD     MiniDisc
+      /A    Analogue transfer from media
+
+    DAT    DAT
+      /A    Analogue transfer from media
+      /1    standard, 48 kHz/16 bits, linear
+      /2    mode 2, 32 kHz/16 bits, linear
+      /3    mode 3, 32 kHz/12 bits, non-linear, low speed
+      /4    mode 4, 32 kHz/12 bits, 4 channels
+      /5    mode 5, 44.1 kHz/16 bits, linear
+      /6    mode 6, 44.1 kHz/16 bits, 'wide track' play
+
+    DCC    DCC
+      /A    Analogue transfer from media
+
+    DVD    DVD
+      /A    Analogue transfer from media
+
+    TV     Television
+      /PAL    PAL
+      /NTSC   NTSC
+      /SECAM  SECAM
+
+    VID    Video
+      /PAL    PAL
+      /NTSC   NTSC
+      /SECAM  SECAM
+      /VHS    VHS
+      /SVHS   S-VHS
+      /BETA   BETAMAX
+
+    RAD    Radio
+      /FM   FM
+      /AM   AM
+      /LW   LW
+      /MW   MW
+
+    TEL    Telephone
+      /I    ISDN
+
+    MC     MC (normal cassette)
+      /4    4.75 cm/s (normal speed for a two sided cassette)
+      /9    9.5 cm/s
+      /I    Type I cassette (ferric/normal)
+      /II   Type II cassette (chrome)
+      /III  Type III cassette (ferric chrome)
+      /IV   Type IV cassette (metal)
+
+    REE    Reel
+      /9    9.5 cm/s
+      /19   19 cm/s
+      /38   38 cm/s
+      /76   76 cm/s
+      /I    Type I cassette (ferric/normal)
+      /II   Type II cassette (chrome)
+      /III  Type III cassette (ferric chrome)
+      /IV   Type IV cassette (metal)
+
+  TMOO
+   The 'Mood' frame is intended to reflect the mood of the audio with a
+   few keywords, e.g. "Romantic" or "Sad".
+
+
+4.2.4.   Rights and license frames
+
+  TCOP
+   The 'Copyright message' frame, in which the string must begin with a
+   year and a space character (making five characters), is intended for
+   the copyright holder of the original sound, not the audio file
+   itself. The absence of this frame means only that the copyright
+   information is unavailable or has been removed, and must not be
+   interpreted to mean that the audio is public domain. Every time this
+   field is displayed the field must be preceded with "Copyright " (C) "
+   ", where (C) is one character showing a C in a circle.
+
+  TPRO
+   The 'Produced notice' frame, in which the string must begin with a
+   year and a space character (making five characters), is intended for
+   the production copyright holder of the original sound, not the audio
+   file itself. The absence of this frame means only that the production
+   copyright information is unavailable or has been removed, and must
+   not be interpreted to mean that the audio is public domain. Every
+   time this field is displayed the field must be preceded with
+   "Produced " (P) " ", where (P) is one character showing a P in a
+   circle.
+
+  TPUB
+   The 'Publisher' frame simply contains the name of the label or
+   publisher.
+
+  TOWN
+   The 'File owner/licensee' frame contains the name of the owner or
+   licensee of the file and it's contents.
+
+  TRSN
+   The 'Internet radio station name' frame contains the name of the
+   internet radio station from which the audio is streamed.
+
+  TRSO
+   The 'Internet radio station owner' frame contains the name of the
+   owner of the internet radio station from which the audio is
+   streamed.
+
+4.2.5.   Other text frames
+
+  TOFN
+   The 'Original filename' frame contains the preferred filename for the
+   file, since some media doesn't allow the desired length of the
+   filename. The filename is case sensitive and includes its suffix.
+
+  TDLY
+   The 'Playlist delay' defines the numbers of milliseconds of silence
+   that should be inserted before this audio. The value zero indicates
+   that this is a part of a multifile audio track that should be played
+   continuously.
+
+  TDEN
+   The 'Encoding time' frame contains a timestamp describing when the
+   audio was encoded. Timestamp format is described in the ID3v2
+   structure document [ID3v2-strct].
+
+  TDOR
+   The 'Original release time' frame contains a timestamp describing
+   when the original recording of the audio was released. Timestamp
+   format is described in the ID3v2 structure document [ID3v2-strct].
+
+  TDRC
+   The 'Recording time' frame contains a timestamp describing when the
+   audio was recorded. Timestamp format is described in the ID3v2
+   structure document [ID3v2-strct].
+
+  TDRL
+   The 'Release time' frame contains a timestamp describing when the
+   audio was first released. Timestamp format is described in the ID3v2
+   structure document [ID3v2-strct].
+
+  TDTG
+   The 'Tagging time' frame contains a timestamp describing then the
+   audio was tagged. Timestamp format is described in the ID3v2
+   structure document [ID3v2-strct].
+
+  TSSE
+   The 'Software/Hardware and settings used for encoding' frame
+   includes the used audio encoder and its settings when the file was
+   encoded. Hardware refers to hardware encoders, not the computer on
+   which a program was run.
+
+  TSOA
+   The 'Album sort order' frame defines a string which should be used
+   instead of the album name (TALB) for sorting purposes. E.g. an album
+   named "A Soundtrack" might preferably be sorted as "Soundtrack".
+
+  TSOP
+   The 'Performer sort order' frame defines a string which should be
+   used instead of the performer (TPE2) for sorting purposes.
+
+  TSOT
+   The 'Title sort order' frame defines a string which should be used
+   instead of the title (TIT2) for sorting purposes.
+
+
+4.2.6.   User defined text information frame
+
+   This frame is intended for one-string text information concerning the
+   audio file in a similar way to the other "T"-frames. The frame body
+   consists of a description of the string, represented as a terminated
+   string, followed by the actual string. There may be more than one
+   "TXXX" frame in each tag, but only one with the same description.
+
+     <Header for 'User defined text information frame', ID: "TXXX">
+     Text encoding     $xx
+     Description       <text string according to encoding> $00 (00)
+     Value             <text string according to encoding>
+
+
+4.3.   URL link frames
+
+   With these frames dynamic data such as webpages with touring
+   information, price information or plain ordinary news can be added to
+   the tag. There may only be one URL [URL] link frame of its kind in an
+   tag, except when stated otherwise in the frame description. If the
+   text string is followed by a string termination, all the following
+   information should be ignored and not be displayed. All URL link
+   frame identifiers begins with "W". Only URL link frame identifiers
+   begins with "W", except for "WXXX". All URL link frames have the
+   following format:
+
+     <Header for 'URL link frame', ID: "W000" - "WZZZ", excluding "WXXX"
+     described in 4.3.2.>
+     URL              <text string>
+
+
+4.3.1.   URL link frames - details
+
+  WCOM
+   The 'Commercial information' frame is a URL pointing at a webpage
+   with information such as where the album can be bought. There may be
+   more than one "WCOM" frame in a tag, but not with the same content.
+
+  WCOP
+   The 'Copyright/Legal information' frame is a URL pointing at a
+   webpage where the terms of use and ownership of the file is
+   described.
+
+  WOAF
+   The 'Official audio file webpage' frame is a URL pointing at a file
+   specific webpage.
+
+  WOAR
+   The 'Official artist/performer webpage' frame is a URL pointing at
+   the artists official webpage. There may be more than one "WOAR" frame
+   in a tag if the audio contains more than one performer, but not with
+   the same content.
+
+  WOAS
+   The 'Official audio source webpage' frame is a URL pointing at the
+   official webpage for the source of the audio file, e.g. a movie.
+
+  WORS
+   The 'Official Internet radio station homepage' contains a URL
+   pointing at the homepage of the internet radio station.
+
+  WPAY
+   The 'Payment' frame is a URL pointing at a webpage that will handle
+   the process of paying for this file.
+
+  WPUB
+   The 'Publishers official webpage' frame is a URL pointing at the
+   official webpage for the publisher.
+
+
+4.3.2.   User defined URL link frame
+
+   This frame is intended for URL [URL] links concerning the audio file
+   in a similar way to the other "W"-frames. The frame body consists
+   of a description of the string, represented as a terminated string,
+   followed by the actual URL. The URL is always encoded with ISO-8859-1
+   [ISO-8859-1]. There may be more than one "WXXX" frame in each tag,
+   but only one with the same description.
+
+     <Header for 'User defined URL link frame', ID: "WXXX">
+     Text encoding     $xx
+     Description       <text string according to encoding> $00 (00)
+     URL               <text string>
+
+
+4.4.   Music CD identifier
+
+   This frame is intended for music that comes from a CD, so that the CD
+   can be identified in databases such as the CDDB [CDDB]. The frame
+   consists of a binary dump of the Table Of Contents, TOC, from the CD,
+   which is a header of 4 bytes and then 8 bytes/track on the CD plus 8
+   bytes for the 'lead out', making a maximum of 804 bytes. The offset
+   to the beginning of every track on the CD should be described with a
+   four bytes absolute CD-frame address per track, and not with absolute
+   time. When this frame is used the presence of a valid "TRCK" frame is
+   REQUIRED, even if the CD's only got one track. It is recommended that
+   this frame is always added to tags originating from CDs. There may
+   only be one "MCDI" frame in each tag.
+
+     <Header for 'Music CD identifier', ID: "MCDI">
+     CD TOC                <binary data>
+
+
+4.5.   Event timing codes
+
+   This frame allows synchronisation with key events in the audio. The
+   header is:
+
+     <Header for 'Event timing codes', ID: "ETCO">
+     Time stamp format    $xx
+
+   Where time stamp format is:
+
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Absolute time means that every stamp contains the time from the
+   beginning of the file.
+
+   Followed by a list of key events in the following format:
+
+     Type of event   $xx
+     Time stamp      $xx (xx ...)
+
+   The 'Time stamp' is set to zero if directly at the beginning of the
+   sound or after the previous event. All events MUST be sorted in
+   chronological order. The type of event is as follows:
+
+     $00  padding (has no meaning)
+     $01  end of initial silence
+     $02  intro start
+     $03  main part start
+     $04  outro start
+     $05  outro end
+     $06  verse start
+     $07  refrain start
+     $08  interlude start
+     $09  theme start
+     $0A  variation start
+     $0B  key change
+     $0C  time change
+     $0D  momentary unwanted noise (Snap, Crackle & Pop)
+     $0E  sustained noise
+     $0F  sustained noise end
+     $10  intro end
+     $11  main part end
+     $12  verse end
+     $13  refrain end
+     $14  theme end
+     $15  profanity
+     $16  profanity end
+
+     $17-$DF  reserved for future use
+
+     $E0-$EF  not predefined synch 0-F
+
+     $F0-$FC  reserved for future use
+
+     $FD  audio end (start of silence)
+     $FE  audio file ends
+     $FF  one more byte of events follows (all the following bytes with
+          the value $FF have the same function)
+
+   Terminating the start events such as "intro start" is OPTIONAL. The
+   'Not predefined synch's ($E0-EF) are for user events. You might want
+   to synchronise your music to something, like setting off an explosion
+   on-stage, activating a screensaver etc.
+
+   There may only be one "ETCO" frame in each tag.
+
+
+4.6.   MPEG location lookup table
+
+   To increase performance and accuracy of jumps within a MPEG [MPEG]
+   audio file, frames with time codes in different locations in the file
+   might be useful. This ID3v2 frame includes references that the
+   software can use to calculate positions in the file. After the frame
+   header follows a descriptor of how much the 'frame counter' should be
+   increased for every reference. If this value is two then the first
+   reference points out the second frame, the 2nd reference the 4th
+   frame, the 3rd reference the 6th frame etc. In a similar way the
+   'bytes between reference' and 'milliseconds between reference' points
+   out bytes and milliseconds respectively.
+
+   Each reference consists of two parts; a certain number of bits, as
+   defined in 'bits for bytes deviation', that describes the difference
+   between what is said in 'bytes between reference' and the reality and
+   a certain number of bits, as defined in 'bits for milliseconds
+   deviation', that describes the difference between what is said in
+   'milliseconds between reference' and the reality. The number of bits
+   in every reference, i.e. 'bits for bytes deviation'+'bits for
+   milliseconds deviation', must be a multiple of four. There may only
+   be one "MLLT" frame in each tag.
+
+     <Header for 'Location lookup table', ID: "MLLT">
+     MPEG frames between reference  $xx xx
+     Bytes between reference        $xx xx xx
+     Milliseconds between reference $xx xx xx
+     Bits for bytes deviation       $xx
+     Bits for milliseconds dev.     $xx
+
+   Then for every reference the following data is included;
+
+     Deviation in bytes         %xxx....
+     Deviation in milliseconds  %xxx....
+
+
+4.7.   Synchronised tempo codes
+
+   For a more accurate description of the tempo of a musical piece, this
+   frame might be used. After the header follows one byte describing
+   which time stamp format should be used. Then follows one or more
+   tempo codes. Each tempo code consists of one tempo part and one time
+   part. The tempo is in BPM described with one or two bytes. If the
+   first byte has the value $FF, one more byte follows, which is added
+   to the first giving a range from 2 - 510 BPM, since $00 and $01 is
+   reserved. $00 is used to describe a beat-free time period, which is
+   not the same as a music-free time period. $01 is used to indicate one
+   single beat-stroke followed by a beat-free period.
+
+   The tempo descriptor is followed by a time stamp. Every time the
+   tempo in the music changes, a tempo descriptor may indicate this for
+   the player. All tempo descriptors MUST be sorted in chronological
+   order. The first beat-stroke in a time-period is at the same time as
+   the beat description occurs. There may only be one "SYTC" frame in
+   each tag.
+
+     <Header for 'Synchronised tempo codes', ID: "SYTC">
+     Time stamp format   $xx
+     Tempo data          <binary data>
+
+   Where time stamp format is:
+
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Absolute time means that every stamp contains the time from the
+   beginning of the file.
+
+
+4.8.   Unsynchronised lyrics/text transcription
+
+   This frame contains the lyrics of the song or a text transcription of
+   other vocal activities. The head includes an encoding descriptor and
+   a content descriptor. The body consists of the actual text. The
+   'Content descriptor' is a terminated string. If no descriptor is
+   entered, 'Content descriptor' is $00 (00) only. Newline characters
+   are allowed in the text. There may be more than one 'Unsynchronised
+   lyrics/text transcription' frame in each tag, but only one with the
+   same language and content descriptor.
+
+     <Header for 'Unsynchronised lyrics/text transcription', ID: "USLT">
+     Text encoding        $xx
+     Language             $xx xx xx
+     Content descriptor   <text string according to encoding> $00 (00)
+     Lyrics/text          <full text string according to encoding>
+
+
+4.9.   Synchronised lyrics/text
+
+   This is another way of incorporating the words, said or sung lyrics,
+   in the audio file as text, this time, however, in sync with the
+   audio. It might also be used to describing events e.g. occurring on a
+   stage or on the screen in sync with the audio. The header includes a
+   content descriptor, represented with as terminated text string. If no
+   descriptor is entered, 'Content descriptor' is $00 (00) only.
+
+     <Header for 'Synchronised lyrics/text', ID: "SYLT">
+     Text encoding        $xx
+     Language             $xx xx xx
+     Time stamp format    $xx
+     Content type         $xx
+     Content descriptor   <text string according to encoding> $00 (00)
+
+   Content type:   $00 is other
+                   $01 is lyrics
+                   $02 is text transcription
+                   $03 is movement/part name (e.g. "Adagio")
+                   $04 is events (e.g. "Don Quijote enters the stage")
+                   $05 is chord (e.g. "Bb F Fsus")
+                   $06 is trivia/'pop up' information
+                   $07 is URLs to webpages
+                   $08 is URLs to images
+
+   Time stamp format:
+
+     $01  Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   Absolute time means that every stamp contains the time from the
+   beginning of the file.
+
+   The text that follows the frame header differs from that of the
+   unsynchronised lyrics/text transcription in one major way. Each
+   syllable (or whatever size of text is considered to be convenient by
+   the encoder) is a null terminated string followed by a time stamp
+   denoting where in the sound file it belongs. Each sync thus has the
+   following structure:
+
+     Terminated text to be synced (typically a syllable)
+     Sync identifier (terminator to above string)   $00 (00)
+     Time stamp                                     $xx (xx ...)
+
+   The 'time stamp' is set to zero or the whole sync is omitted if
+   located directly at the beginning of the sound. All time stamps
+   should be sorted in chronological order. The sync can be considered
+   as a validator of the subsequent string.
+
+   Newline characters are allowed in all "SYLT" frames and MUST be used
+   after every entry (name, event etc.) in a frame with the content type
+   $03 - $04.
+
+   A few considerations regarding whitespace characters: Whitespace
+   separating words should mark the beginning of a new word, thus
+   occurring in front of the first syllable of a new word. This is also
+   valid for new line characters. A syllable followed by a comma should
+   not be broken apart with a sync (both the syllable and the comma
+   should be before the sync).
+
+   An example: The "USLT" passage
+
+     "Strangers in the night" $0A "Exchanging glances"
+
+   would be "SYLT" encoded as:
+
+     "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx
+     " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx
+     xx "glan" $00 xx xx "ces" $00 xx xx
+
+   There may be more than one "SYLT" frame in each tag, but only one
+   with the same language and content descriptor.
+
+
+4.10.   Comments
+
+   This frame is intended for any kind of full text information that
+   does not fit in any other frame. It consists of a frame header
+   followed by encoding, language and content descriptors and is ended
+   with the actual comment as a text string. Newline characters are
+   allowed in the comment text string. There may be more than one
+   comment frame in each tag, but only one with the same language and
+   content descriptor.
+
+     <Header for 'Comment', ID: "COMM">
+     Text encoding          $xx
+     Language               $xx xx xx
+     Short content descrip. <text string according to encoding> $00 (00)
+     The actual text        <full text string according to encoding>
+
+
+4.11.   Relative volume adjustment (2)
+
+   This is a more subjective frame than the previous ones. It allows the
+   user to say how much he wants to increase/decrease the volume on each
+   channel when the file is played. The purpose is to be able to align
+   all files to a reference volume, so that you don't have to change the
+   volume constantly. This frame may also be used to balance adjust the
+   audio. The volume adjustment is encoded as a fixed point decibel
+   value, 16 bit signed integer representing (adjustment*512), giving
+   +/- 64 dB with a precision of 0.001953125 dB. E.g. +2 dB is stored as
+   $04 00 and -2 dB is $FC 00. There may be more than one "RVA2" frame
+   in each tag, but only one with the same identification string.
+
+     <Header for 'Relative volume adjustment (2)', ID: "RVA2">
+     Identification          <text string> $00
+
+   The 'identification' string is used to identify the situation and/or
+   device where this adjustment should apply. The following is then
+   repeated for every channel
+
+     Type of channel         $xx
+     Volume adjustment       $xx xx
+     Bits representing peak  $xx
+     Peak volume             $xx (xx ...)
+
+
+   Type of channel:  $00  Other
+                     $01  Master volume
+                     $02  Front right
+                     $03  Front left
+                     $04  Back right
+                     $05  Back left
+                     $06  Front centre
+                     $07  Back centre
+                     $08  Subwoofer
+
+   Bits representing peak can be any number between 0 and 255. 0 means
+   that there is no peak volume field. The peak volume field is always
+   padded to whole bytes, setting the most significant bits to zero.
+
+
+4.12.   Equalisation (2)
+
+   This is another subjective, alignment frame. It allows the user to
+   predefine an equalisation curve within the audio file. There may be
+   more than one "EQU2" frame in each tag, but only one with the same
+   identification string.
+
+     <Header of 'Equalisation (2)', ID: "EQU2">
+     Interpolation method  $xx
+     Identification        <text string> $00
+
+   The 'interpolation method' describes which method is preferred when
+   an interpolation between the adjustment point that follows. The
+   following methods are currently defined:
+
+     $00  Band
+          No interpolation is made. A jump from one adjustment level to
+          another occurs in the middle between two adjustment points.
+     $01  Linear
+          Interpolation between adjustment points is linear.
+
+   The 'identification' string is used to identify the situation and/or
+   device where this adjustment should apply. The following is then
+   repeated for every adjustment point
+
+     Frequency          $xx xx
+     Volume adjustment  $xx xx
+
+   The frequency is stored in units of 1/2 Hz, giving it a range from 0
+   to 32767 Hz.
+
+   The volume adjustment is encoded as a fixed point decibel value, 16
+   bit signed integer representing (adjustment*512), giving +/- 64 dB
+   with a precision of 0.001953125 dB. E.g. +2 dB is stored as $04 00
+   and -2 dB is $FC 00.
+
+   Adjustment points should be ordered by frequency and one frequency
+   should only be described once in the frame.
+
+
+4.13.   Reverb
+
+   Yet another subjective frame, with which you can adjust echoes of
+   different kinds. Reverb left/right is the delay between every bounce
+   in ms. Reverb bounces left/right is the number of bounces that should
+   be made. $FF equals an infinite number of bounces. Feedback is the
+   amount of volume that should be returned to the next echo bounce. $00
+   is 0%, $FF is 100%. If this value were $7F, there would be 50% volume
+   reduction on the first bounce, 50% of that on the second and so on.
+   Left to left means the sound from the left bounce to be played in the
+   left speaker, while left to right means sound from the left bounce to
+   be played in the right speaker.
+
+   'Premix left to right' is the amount of left sound to be mixed in the
+   right before any reverb is applied, where $00 id 0% and $FF is 100%.
+   'Premix right to left' does the same thing, but right to left.
+   Setting both premix to $FF would result in a mono output (if the
+   reverb is applied symmetric). There may only be one "RVRB" frame in
+   each tag.
+
+     <Header for 'Reverb', ID: "RVRB">
+     Reverb left (ms)                 $xx xx
+     Reverb right (ms)                $xx xx
+     Reverb bounces, left             $xx
+     Reverb bounces, right            $xx
+     Reverb feedback, left to left    $xx
+     Reverb feedback, left to right   $xx
+     Reverb feedback, right to right  $xx
+     Reverb feedback, right to left   $xx
+     Premix left to right             $xx
+     Premix right to left             $xx
+
+
+4.14.   Attached picture
+
+   This frame contains a picture directly related to the audio file.
+   Image format is the MIME type and subtype [MIME] for the image. In
+   the event that the MIME media type name is omitted, "image/" will be
+   implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format
+   should be used when interoperability is wanted. Description is a
+   short description of the picture, represented as a terminated
+   text string. There may be several pictures attached to one file, each
+   in their individual "APIC" frame, but only one with the same content
+   descriptor. There may only be one picture with the picture type
+   declared as picture type $01 and $02 respectively. There is the
+   possibility to put only a link to the image file by using the 'MIME
+   type' "-->" and having a complete URL [URL] instead of picture data.
+   The use of linked files should however be used sparingly since there
+   is the risk of separation of files.
+
+     <Header for 'Attached picture', ID: "APIC">
+     Text encoding      $xx
+     MIME type          <text string> $00
+     Picture type       $xx
+     Description        <text string according to encoding> $00 (00)
+     Picture data       <binary data>
+
+
+   Picture type:  $00  Other
+                  $01  32x32 pixels 'file icon' (PNG only)
+                  $02  Other file icon
+                  $03  Cover (front)
+                  $04  Cover (back)
+                  $05  Leaflet page
+                  $06  Media (e.g. label side of CD)
+                  $07  Lead artist/lead performer/soloist
+                  $08  Artist/performer
+                  $09  Conductor
+                  $0A  Band/Orchestra
+                  $0B  Composer
+                  $0C  Lyricist/text writer
+                  $0D  Recording Location
+                  $0E  During recording
+                  $0F  During performance
+                  $10  Movie/video screen capture
+                  $11  A bright coloured fish
+                  $12  Illustration
+                  $13  Band/artist logotype
+                  $14  Publisher/Studio logotype
+
+
+4.15.   General encapsulated object
+
+   In this frame any type of file can be encapsulated. After the header,
+   'Frame size' and 'Encoding' follows 'MIME type' [MIME] represented as
+   as a terminated string encoded with ISO 8859-1 [ISO-8859-1]. The
+   filename is case sensitive and is encoded as 'Encoding'. Then follows
+   a content description as terminated string, encoded as 'Encoding'.
+   The last thing in the frame is the actual object. The first two
+   strings may be omitted, leaving only their terminations. MIME type is
+   always an ISO-8859-1 text string. There may be more than one "GEOB"
+   frame in each tag, but only one with the same content descriptor.
+
+     <Header for 'General encapsulated object', ID: "GEOB">
+     Text encoding          $xx
+     MIME type              <text string> $00
+     Filename               <text string according to encoding> $00 (00)
+     Content description    <text string according to encoding> $00 (00)
+     Encapsulated object    <binary data>
+
+
+4.16.   Play counter
+
+   This is simply a counter of the number of times a file has been
+   played. The value is increased by one every time the file begins to
+   play. There may only be one "PCNT" frame in each tag. When the
+   counter reaches all one's, one byte is inserted in front of the
+   counter thus making the counter eight bits bigger.  The counter must
+   be at least 32-bits long to begin with.
+
+     <Header for 'Play counter', ID: "PCNT">
+     Counter        $xx xx xx xx (xx ...)
+
+
+4.17.   Popularimeter
+
+   The purpose of this frame is to specify how good an audio file is.
+   Many interesting applications could be found to this frame such as a
+   playlist that features better audio files more often than others or
+   it could be used to profile a person's taste and find other 'good'
+   files by comparing people's profiles. The frame contains the email
+   address to the user, one rating byte and a four byte play counter,
+   intended to be increased with one for every time the file is played.
+   The email is a terminated string. The rating is 1-255 where 1 is
+   worst and 255 is best. 0 is unknown. If no personal counter is wanted
+   it may be omitted. When the counter reaches all one's, one byte is
+   inserted in front of the counter thus making the counter eight bits
+   bigger in the same away as the play counter ("PCNT"). There may be
+   more than one "POPM" frame in each tag, but only one with the same
+   email address.
+
+     <Header for 'Popularimeter', ID: "POPM">
+     Email to user   <text string> $00
+     Rating          $xx
+     Counter         $xx xx xx xx (xx ...)
+
+
+4.18.   Recommended buffer size
+
+   Sometimes the server from which an audio file is streamed is aware of
+   transmission or coding problems resulting in interruptions in the
+   audio stream. In these cases, the size of the buffer can be
+   recommended by the server using this frame. If the 'embedded info
+   flag' is true (1) then this indicates that an ID3 tag with the
+   maximum size described in 'Buffer size' may occur in the audio
+   stream. In such case the tag should reside between two MPEG [MPEG]
+   frames, if the audio is MPEG encoded. If the position of the next tag
+   is known, 'offset to next tag' may be used. The offset is calculated
+   from the end of tag in which this frame resides to the first byte of
+   the header in the next. This field may be omitted. Embedded tags are
+   generally not recommended since this could render unpredictable
+   behaviour from present software/hardware.
+
+   For applications like streaming audio it might be an idea to embed
+   tags into the audio stream though. If the clients connects to
+   individual connections like HTTP and there is a possibility to begin
+   every transmission with a tag, then this tag should include a
+   'recommended buffer size' frame. If the client is connected to a
+   arbitrary point in the stream, such as radio or multicast, then the
+   'recommended buffer size' frame SHOULD be included in every tag.
+
+   The 'Buffer size' should be kept to a minimum. There may only be one
+   "RBUF" frame in each tag.
+
+     <Header for 'Recommended buffer size', ID: "RBUF">
+     Buffer size               $xx xx xx
+     Embedded info flag        %0000000x
+     Offset to next tag        $xx xx xx xx
+
+
+4.19.   Audio encryption
+
+   This frame indicates if the actual audio stream is encrypted, and by
+   whom. Since standardisation of such encryption scheme is beyond this
+   document, all "AENC" frames begin with a terminated string with a
+   URL containing an email address, or a link to a location where an
+   email address can be found, that belongs to the organisation
+   responsible for this specific encrypted audio file. Questions
+   regarding the encrypted audio should be sent to the email address
+   specified. If a $00 is found directly after the 'Frame size' and the
+   audio file indeed is encrypted, the whole file may be considered
+   useless.
+
+   After the 'Owner identifier', a pointer to an unencrypted part of the
+   audio can be specified. The 'Preview start' and 'Preview length' is
+   described in frames. If no part is unencrypted, these fields should
+   be left zeroed. After the 'preview length' field follows optionally a
+   data block required for decryption of the audio. There may be more
+   than one "AENC" frames in a tag, but only one with the same 'Owner
+   identifier'.
+
+     <Header for 'Audio encryption', ID: "AENC">
+     Owner identifier   <text string> $00
+     Preview start      $xx xx
+     Preview length     $xx xx
+     Encryption info    <binary data>
+
+
+4.20.   Linked information
+
+   To keep information duplication as low as possible this frame may be
+   used to link information from another ID3v2 tag that might reside in
+   another audio file or alone in a binary file. It is RECOMMENDED that
+   this method is only used when the files are stored on a CD-ROM or
+   other circumstances when the risk of file separation is low. The
+   frame contains a frame identifier, which is the frame that should be
+   linked into this tag, a URL [URL] field, where a reference to the
+   file where the frame is given, and additional ID data, if needed.
+   Data should be retrieved from the first tag found in the file to
+   which this link points. There may be more than one "LINK" frame in a
+   tag, but only one with the same contents. A linked frame is to be
+   considered as part of the tag and has the same restrictions as if it
+   was a physical part of the tag (i.e. only one "RVRB" frame allowed,
+   whether it's linked or not).
+
+     <Header for 'Linked information', ID: "LINK">
+     Frame identifier        $xx xx xx xx
+     URL                     <text string> $00
+     ID and additional data  <text string(s)>
+
+   Frames that may be linked and need no additional data are "ASPI",
+   "ETCO", "EQU2", "MCID", "MLLT", "OWNE", "RVA2", "RVRB", "SYTC", the
+   text information frames and the URL link frames.
+
+   The "AENC", "APIC", "GEOB" and "TXXX" frames may be linked with
+   the content descriptor as additional ID data.
+
+   The "USER" frame may be linked with the language field as additional
+   ID data.
+   
+   The "PRIV" frame may be linked with the owner identifier as
+   additional ID data.
+
+   The "COMM", "SYLT" and "USLT" frames may be linked with three bytes
+   of language descriptor directly followed by a content descriptor as
+   additional ID data.
+
+
+4.21.   Position synchronisation frame
+
+   This frame delivers information to the listener of how far into the
+   audio stream he picked up; in effect, it states the time offset from
+   the first frame in the stream. The frame layout is:
+
+     <Head for 'Position synchronisation', ID: "POSS">
+     Time stamp format         $xx
+     Position                  $xx (xx ...)
+
+   Where time stamp format is:
+
+     $01  Absolute time, 32 bit sized, using MPEG frames as unit
+     $02  Absolute time, 32 bit sized, using milliseconds as unit
+
+   and position is where in the audio the listener starts to receive,
+   i.e. the beginning of the next frame. If this frame is used in the
+   beginning of a file the value is always 0. There may only be one
+   "POSS" frame in each tag.
+
+
+4.22.   Terms of use frame
+
+   This frame contains a brief description of the terms of use and
+   ownership of the file. More detailed information concerning the legal
+   terms might be available through the "WCOP" frame. Newlines are
+   allowed in the text. There may be more than one 'Terms of use' frame
+   in a tag, but only one with the same 'Language'.
+
+     <Header for 'Terms of use frame', ID: "USER">
+     Text encoding        $xx
+     Language             $xx xx xx
+     The actual text      <text string according to encoding>
+
+
+4.23.   Ownership frame
+
+   The ownership frame might be used as a reminder of a made transaction
+   or, if signed, as proof. Note that the "USER" and "TOWN" frames are
+   good to use in conjunction with this one. The frame begins, after the
+   frame ID, size and encoding fields, with a 'price paid' field. The
+   first three characters of this field contains the currency used for
+   the transaction, encoded according to ISO 4217 [ISO-4217] alphabetic
+   currency code. Concatenated to this is the actual price paid, as a
+   numerical string using "." as the decimal separator. Next is an 8
+   character date string (YYYYMMDD) followed by a string with the name
+   of the seller as the last field in the frame. There may only be one
+   "OWNE" frame in a tag.
+
+     <Header for 'Ownership frame', ID: "OWNE">
+     Text encoding     $xx
+     Price paid        <text string> $00
+     Date of purch.    <text string>
+     Seller            <text string according to encoding>
+
+
+4.24.   Commercial frame
+
+   This frame enables several competing offers in the same tag by
+   bundling all needed information. That makes this frame rather complex
+   but it's an easier solution than if one tries to achieve the same
+   result with several frames. The frame begins, after the frame ID,
+   size and encoding fields, with a price string field. A price is
+   constructed by one three character currency code, encoded according
+   to ISO 4217 [ISO-4217] alphabetic currency code, followed by a
+   numerical value where "." is used as decimal separator. In the price
+   string several prices may be concatenated, separated by a "/"
+   character, but there may only be one currency of each type.
+
+   The price string is followed by an 8 character date string in the
+   format YYYYMMDD, describing for how long the price is valid. After
+   that is a contact URL, with which the user can contact the seller,
+   followed by a one byte 'received as' field. It describes how the
+   audio is delivered when bought according to the following list:
+
+        $00  Other
+        $01  Standard CD album with other songs
+        $02  Compressed audio on CD
+        $03  File over the Internet
+        $04  Stream over the Internet
+        $05  As note sheets
+        $06  As note sheets in a book with other sheets
+        $07  Music on other media
+        $08  Non-musical merchandise
+
+   Next follows a terminated string with the name of the seller followed
+   by a terminated string with a short description of the product. The
+   last thing is the ability to include a company logotype. The first of
+   them is the 'Picture MIME type' field containing information about
+   which picture format is used. In the event that the MIME media type
+   name is omitted, "image/" will be implied. Currently only "image/png"
+   and "image/jpeg" are allowed. This format string is followed by the
+   binary picture data. This two last fields may be omitted if no
+   picture is attached. There may be more than one 'commercial frame' in
+   a tag, but no two may be identical.
+
+     <Header for 'Commercial frame', ID: "COMR">
+     Text encoding      $xx
+     Price string       <text string> $00
+     Valid until        <text string>
+     Contact URL        <text string> $00
+     Received as        $xx
+     Name of seller     <text string according to encoding> $00 (00)
+     Description        <text string according to encoding> $00 (00)
+     Picture MIME type  <string> $00
+     Seller logo        <binary data>
+
+
+4.25.   Encryption method registration
+
+   To identify with which method a frame has been encrypted the
+   encryption method must be registered in the tag with this frame. The
+   'Owner identifier' is a null-terminated string with a URL [URL]
+   containing an email address, or a link to a location where an email
+   address can be found, that belongs to the organisation responsible
+   for this specific encryption method. Questions regarding the
+   encryption method should be sent to the indicated email address. The
+   'Method symbol' contains a value that is associated with this method
+   throughout the whole tag, in the range $80-F0. All other values are
+   reserved. The 'Method symbol' may optionally be followed by
+   encryption specific data. There may be several "ENCR" frames in a tag
+   but only one containing the same symbol and only one containing the
+   same owner identifier. The method must be used somewhere in the tag.
+   See the description of the frame encryption flag in the ID3v2
+   structure document [ID3v2-strct] for more information.
+
+     <Header for 'Encryption method registration', ID: "ENCR">
+     Owner identifier    <text string> $00
+     Method symbol       $xx
+     Encryption data     <binary data>
+
+
+4.26.   Group identification registration
+
+   This frame enables grouping of otherwise unrelated frames. This can
+   be used when some frames are to be signed. To identify which frames
+   belongs to a set of frames a group identifier must be registered in
+   the tag with this frame. The 'Owner identifier' is a null-terminated
+   string with a URL [URL] containing an email address, or a link to a
+   location where an email address can be found, that belongs to the
+   organisation responsible for this grouping. Questions regarding the
+   grouping should be sent to the indicated email address. The 'Group
+   symbol' contains a value that associates the frame with this group
+   throughout the whole tag, in the range $80-F0. All other values are
+   reserved. The 'Group symbol' may optionally be followed by some group
+   specific data, e.g. a digital signature. There may be several "GRID"
+   frames in a tag but only one containing the same symbol and only one
+   containing the same owner identifier. The group symbol must be used
+   somewhere in the tag. See the description of the frame grouping flag
+   in the ID3v2 structure document [ID3v2-strct] for more information.
+
+     <Header for 'Group ID registration', ID: "GRID">
+     Owner identifier      <text string> $00
+     Group symbol          $xx
+     Group dependent data  <binary data>
+
+
+4.27.   Private frame
+
+   This frame is used to contain information from a software producer
+   that its program uses and does not fit into the other frames. The
+   frame consists of an 'Owner identifier' string and the binary data.
+   The 'Owner identifier' is a null-terminated string with a URL [URL]
+   containing an email address, or a link to a location where an email
+   address can be found, that belongs to the organisation responsible
+   for the frame. Questions regarding the frame should be sent to the
+   indicated email address. The tag may contain more than one "PRIV"
+   frame but only with different contents.
+
+     <Header for 'Private frame', ID: "PRIV">
+     Owner identifier      <text string> $00
+     The private data      <binary data>
+
+
+4.28.   Signature frame
+
+   This frame enables a group of frames, grouped with the 'Group
+   identification registration', to be signed. Although signatures can
+   reside inside the registration frame, it might be desired to store
+   the signature elsewhere, e.g. in watermarks. There may be more than
+   one 'signature frame' in a tag, but no two may be identical.
+
+     <Header for 'Signature frame', ID: "SIGN">
+     Group symbol      $xx
+     Signature         <binary data>
+
+
+4.29.   Seek frame
+
+   This frame indicates where other tags in a file/stream can be found.
+   The 'minimum offset to next tag' is calculated from the end of this
+   tag to the beginning of the next. There may only be one 'seek frame'
+   in a tag.
+
+   <Header for 'Seek frame', ID: "SEEK">
+   Minimum offset to next tag       $xx xx xx xx
+
+
+4.30.   Audio seek point index
+
+   Audio files with variable bit rates are intrinsically difficult to
+   deal with in the case of seeking within the file. The ASPI frame
+   makes seeking easier by providing a list a seek points within the
+   audio file. The seek points are a fractional offset within the audio
+   data, providing a starting point from which to find an appropriate
+   point to start decoding. The presence of an ASPI frame requires the
+   existence of a TLEN frame, indicating the duration of the file in
+   milliseconds. There may only be one 'audio seek point index' frame in
+   a tag.
+
+     <Header for 'Seek Point Index', ID: "ASPI">
+     Indexed data start (S)         $xx xx xx xx
+     Indexed data length (L)        $xx xx xx xx
+     Number of index points (N)     $xx xx
+     Bits per index point (b)       $xx
+
+   Then for every index point the following data is included;
+
+     Fraction at index (Fi)          $xx (xx)
+
+   'Indexed data start' is a byte offset from the beginning of the file.
+   'Indexed data length' is the byte length of the audio data being
+   indexed. 'Number of index points' is the number of index points, as
+   the name implies. The recommended number is 100. 'Bits per index
+   point' is 8 or 16, depending on the chosen precision. 8 bits works
+   well for short files (less than 5 minutes of audio), while 16 bits is
+   advantageous for long files. 'Fraction at index' is the numerator of
+   the fraction representing a relative position in the data. The
+   denominator is 2 to the power of b.
+
+   Here are the algorithms to be used in the calculation. The known data
+   must be the offset of the start of the indexed data (S), the offset
+   of the end of the indexed data (E), the number of index points (N),
+   the offset at index i (Oi). We calculate the fraction at index i
+   (Fi).
+
+   Oi is the offset of the frame whose start is soonest after the point
+   for which the time offset is (i/N * duration).
+
+   The frame data should be calculated as follows:
+
+     Fi = Oi/L * 2^b    (rounded down to the nearest integer)
+
+   Offset calculation should be calculated as follows from data in the
+   frame:
+
+     Oi = (Fi/2^b)*L    (rounded up to the nearest integer)
+
+
+5.  Copyright
+
+   Copyright (C) Martin Nilsson 2000. All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that a reference to this document is included on all
+   such copies and derivative works. However, this document itself may
+   not be modified in any way and reissued as the original document.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+   THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+   WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+6.   References
+
+   [CDDB] Compact Disc Data Base
+
+      <url:http://www.cddb.com>
+
+   [ID3v2.3.0] Martin Nilsson, "ID3v2 informal standard".
+
+      <url:http://www.id3.org/id3v2.3.0.txt>
+
+   [ID3v2-strct] Martin Nilsson,
+   "ID3 tag version 2.4.0 - Main Structure"
+   
+      <url:http//www.id3.org/id3v2.4.0-structure.txt>
+
+   [ISO-639-2] ISO/FDIS 639-2.
+   Codes for the representation of names of languages, Part 2: Alpha-3
+   code. Technical committee / subcommittee: TC 37 / SC 2
+
+   [ISO-4217] ISO 4217:1995.
+   Codes for the representation of currencies and funds.
+   Technical committee / subcommittee: TC 68
+
+   [ISO-8859-1] ISO/IEC DIS 8859-1.
+   8-bit single-byte coded graphic character sets, Part 1: Latin
+   alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2
+
+   [ISRC] ISO 3901:1986
+   International Standard Recording Code (ISRC).
+   Technical committee / subcommittee: TC 46 / SC 9
+
+   [JFIF] JPEG File Interchange Format, version 1.02
+
+      <url:http://www.w3.org/Graphics/JPEG/jfif.txt>
+
+   [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate
+   Requirement Levels', RFC 2119, March 1997.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc2119.txt>
+
+   [MIME] Freed, N.  and N. Borenstein,  "Multipurpose Internet Mail
+   Extensions (MIME) Part One: Format of Internet Message Bodies",
+   RFC 2045, November 1996.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc2045.txt>
+
+   [MPEG] ISO/IEC 11172-3:1993.
+   Coding of moving pictures and associated audio for digital storage
+   media at up to about 1,5 Mbit/s, Part 3: Audio.
+   Technical committee / subcommittee: JTC 1 / SC 29
+    and
+   ISO/IEC 13818-3:1995
+   Generic coding of moving pictures and associated audio information,
+   Part 3: Audio.
+   Technical committee / subcommittee: JTC 1 / SC 29
+    and
+   ISO/IEC DIS 13818-3
+   Generic coding of moving pictures and associated audio information,
+   Part 3: Audio (Revision of ISO/IEC 13818-3:1995)
+
+
+   [PNG] Portable Network Graphics, version 1.0
+
+      <url:http://www.w3.org/TR/REC-png-multi.html>
+
+   [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource
+   Locators (URL).", RFC 1738, December 1994.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
+
+   [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, "ZLIB
+   Compressed
+   Data Format Specification version 3.3", RFC 1950, May 1996.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc1950.txt>
+
+
+7.   Appendix
+
+
+A.   Appendix A - Genre List from ID3v1
+
+   The following genres is defined in ID3v1
+
+      0.Blues
+      1.Classic Rock
+      2.Country
+      3.Dance
+      4.Disco
+      5.Funk
+      6.Grunge
+      7.Hip-Hop
+      8.Jazz
+      9.Metal
+     10.New Age
+     11.Oldies
+     12.Other
+     13.Pop
+     14.R&B
+     15.Rap
+     16.Reggae
+     17.Rock
+     18.Techno
+     19.Industrial
+     20.Alternative
+     21.Ska
+     22.Death Metal
+     23.Pranks
+     24.Soundtrack
+     25.Euro-Techno
+     26.Ambient
+     27.Trip-Hop
+     28.Vocal
+     29.Jazz+Funk
+     30.Fusion
+     31.Trance
+     32.Classical
+     33.Instrumental
+     34.Acid
+     35.House
+     36.Game
+     37.Sound Clip
+     38.Gospel
+     39.Noise
+     40.AlternRock
+     41.Bass
+     42.Soul
+     43.Punk
+     44.Space
+     45.Meditative
+     46.Instrumental Pop
+     47.Instrumental Rock
+     48.Ethnic
+     49.Gothic
+     50.Darkwave
+     51.Techno-Industrial
+     52.Electronic
+     53.Pop-Folk
+     54.Eurodance
+     55.Dream
+     56.Southern Rock
+     57.Comedy
+     58.Cult
+     59.Gangsta
+     60.Top 40
+     61.Christian Rap
+     62.Pop/Funk
+     63.Jungle
+     64.Native American
+     65.Cabaret
+     66.New Wave
+     67.Psychadelic
+     68.Rave
+     69.Showtunes
+     70.Trailer
+     71.Lo-Fi
+     72.Tribal
+     73.Acid Punk
+     74.Acid Jazz
+     75.Polka
+     76.Retro
+     77.Musical
+     78.Rock & Roll
+     79.Hard Rock
+
+
+8.   Author's Address
+
+   Written by
+
+     Martin Nilsson
+     Rydsvägen 246 C. 30
+     SE-584 34 Linköping
+     Sweden
+
+     Email: nilsson@id3.org
diff --git a/src/taglib/mpeg/id3v2/id3v2.4.0-structure.txt b/src/taglib/mpeg/id3v2/id3v2.4.0-structure.txt
new file mode 100644 (file)
index 0000000..5fa156a
--- /dev/null
@@ -0,0 +1,733 @@
+
+Informal standard                                             M. Nilsson
+Document: id3v2.4.0-structure.txt                      16 September 2001
+
+
+                 ID3 tag version 2.4.0 - Main Structure
+
+Status of this document
+
+   This document is an informal standard and replaces the ID3v2.3.0
+   standard [ID3v2]. A formal standard will use another revision number
+   even if the content is identical to document. The contents in this
+   document may change for clarifications but never for added or altered
+   functionallity.
+
+   Distribution of this document is unlimited.
+
+
+Abstract
+
+   This document describes the main structure of ID3v2.4.0, which is a
+   revised version of the ID3v2 informal standard [ID3v2] version
+   2.3.0. The ID3v2 offers a flexible way of storing audio meta
+   information within the audio file itself. The information may be
+   technical information, such as equalisation curves, as well as
+   title, performer, copyright etc.
+
+   ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order
+   to allow for implementations to be revised as easily as possible.
+
+
+1.   Table of contents
+
+        Status of this document
+        Abstract
+   1.   Table of contents
+   2.   Conventions in this document
+   2.   Standard overview
+   3.   ID3v2 overview
+     3.1.   ID3v2 header
+     3.2.   ID3v2 extended header
+     3.3.   Padding
+     3.4.   ID3v2 footer
+   4.   ID3v2 frames overview
+     4.1.   Frame header flags
+       4.1.1. Frame status flags
+       4.1.2. Frame format flags
+   5.   Tag location
+   6.   Unsynchronisation
+     6.1.   The unsynchronisation scheme
+     6.2.   Synchsafe integers
+   7.   Copyright
+   8.   References
+   9.   Author's Address
+
+
+2.   Conventions in this document
+
+   Text within "" is a text string exactly as it appears in a tag.
+   Numbers preceded with $ are hexadecimal and numbers preceded with %
+   are binary. $xx is used to indicate a byte with unknown content. %x
+   is used to indicate a bit with unknown content. The most significant
+   bit (MSB) of a byte is called 'bit 7' and the least significant bit
+   (LSB) is called 'bit 0'.
+
+   A tag is the whole tag described in this document. A frame is a block
+   of information in the tag. The tag consists of a header, frames and
+   optional padding. A field is a piece of information; one value, a
+   string etc. A numeric string is a string that consists of the
+   characters "0123456789" only.
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED",  "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+
+3.   ID3v2 overview
+
+   ID3v2 is a general tagging format for audio, which makes it possible
+   to store meta data about the audio inside the audio file itself. The
+   ID3 tag described in this document is mainly targeted at files
+   encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III
+   and MPEG-2.5, but may work with other types of encoded audio or as a
+   stand alone format for audio meta data.
+
+   ID3v2 is designed to be as flexible and expandable as possible to
+   meet new meta information needs that might arise. To achieve that
+   ID3v2 is constructed as a container for several information blocks,
+   called frames, whose format need not be known to the software that
+   encounters them. At the start of every frame is an unique and
+   predefined identifier, a size descriptor that allows software to skip
+   unknown frames and a flags field. The flags describes encoding
+   details and if the frame should remain in the tag, should it be
+   unknown to the software, if the file is altered.
+
+   The bitorder in ID3v2 is most significant bit first (MSB). The
+   byteorder in multibyte numbers is most significant byte first (e.g.
+   $12345678 would be encoded $12 34 56 78), also known as big endian
+   and network byte order.
+
+   Overall tag structure:
+
+     +-----------------------------+
+     |      Header (10 bytes)      |
+     +-----------------------------+
+     |       Extended Header       |
+     | (variable length, OPTIONAL) |
+     +-----------------------------+
+     |   Frames (variable length)  |
+     +-----------------------------+
+     |           Padding           |
+     | (variable length, OPTIONAL) |
+     +-----------------------------+
+     | Footer (10 bytes, OPTIONAL) |
+     +-----------------------------+
+
+   In general, padding and footer are mutually exclusive. See details in
+   sections 3.3, 3.4 and 5.
+
+
+3.1.   ID3v2 header
+
+   The first part of the ID3v2 tag is the 10 byte tag header, laid out
+   as follows:
+
+     ID3v2/file identifier      "ID3"
+     ID3v2 version              $04 00
+     ID3v2 flags                %abcd0000
+     ID3v2 size             4 * %0xxxxxxx
+
+   The first three bytes of the tag are always "ID3", to indicate that
+   this is an ID3v2 tag, directly followed by the two version bytes. The
+   first byte of ID3v2 version is its major version, while the second
+   byte is its revision number. In this case this is ID3v2.4.0. All
+   revisions are backwards compatible while major versions are not. If
+   software with ID3v2.4.0 and below support should encounter version
+   five or higher it should simply ignore the whole tag. Version or
+   revision will never be $FF.
+
+   The version is followed by the ID3v2 flags field, of which currently
+   four flags are used.
+
+
+   a - Unsynchronisation
+
+     Bit 7 in the 'ID3v2 flags' indicates whether or not
+     unsynchronisation is applied on all frames (see section 6.1 for
+     details); a set bit indicates usage.
+
+
+   b - Extended header
+
+     The second bit (bit 6) indicates whether or not the header is
+     followed by an extended header. The extended header is described in
+     section 3.2. A set bit indicates the presence of an extended
+     header.
+
+
+   c - Experimental indicator
+
+     The third bit (bit 5) is used as an 'experimental indicator'. This
+     flag SHALL always be set when the tag is in an experimental stage.
+
+
+   d - Footer present
+
+     Bit 4 indicates that a footer (section 3.4) is present at the very
+     end of the tag. A set bit indicates the presence of a footer.
+
+
+   All the other flags MUST be cleared. If one of these undefined flags
+   are set, the tag might not be readable for a parser that does not
+   know the flags function.
+
+   The ID3v2 tag size is stored as a 32 bit synchsafe integer (section
+   6.2), making a total of 28 effective bits (representing up to 256MB).
+
+   The ID3v2 tag size is the sum of the byte length of the extended
+   header, the padding and the frames after unsynchronisation. If a
+   footer is present this equals to ('total size' - 20) bytes, otherwise
+   ('total size' - 10) bytes.
+
+   An ID3v2 tag can be detected with the following pattern:
+     $49 44 33 yy yy xx zz zz zz zz
+   Where yy is less than $FF, xx is the 'flags' byte and zz is less than
+   $80.
+
+
+3.2. Extended header
+
+   The extended header contains information that can provide further
+   insight in the structure of the tag, but is not vital to the correct
+   parsing of the tag information; hence the extended header is
+   optional.
+
+     Extended header size   4 * %0xxxxxxx
+     Number of flag bytes       $01
+     Extended Flags             $xx
+
+   Where the 'Extended header size' is the size of the whole extended
+   header, stored as a 32 bit synchsafe integer. An extended header can
+   thus never have a size of fewer than six bytes.
+
+   The extended flags field, with its size described by 'number of flag
+   bytes', is defined as:
+
+     %0bcd0000
+
+   Each flag that is set in the extended header has data attached, which
+   comes in the order in which the flags are encountered (i.e. the data
+   for flag 'b' comes before the data for flag 'c'). Unset flags cannot
+   have any attached data. All unknown flags MUST be unset and their
+   corresponding data removed when a tag is modified.
+
+   Every set flag's data starts with a length byte, which contains a
+   value between 0 and 127 ($00 - $7f), followed by data that has the
+   field length indicated by the length byte. If a flag has no attached
+   data, the value $00 is used as length byte.
+
+
+   b - Tag is an update
+
+     If this flag is set, the present tag is an update of a tag found
+     earlier in the present file or stream. If frames defined as unique
+     are found in the present tag, they are to override any
+     corresponding ones found in the earlier tag. This flag has no
+     corresponding data.
+
+         Flag data length      $00
+
+   c - CRC data present
+
+     If this flag is set, a CRC-32 [ISO-3309] data is included in the
+     extended header. The CRC is calculated on all the data between the
+     header and footer as indicated by the header's tag length field,
+     minus the extended header. Note that this includes the padding (if
+     there is any), but excludes the footer. The CRC-32 is stored as an
+     35 bit synchsafe integer, leaving the upper four bits always
+     zeroed.
+
+        Flag data length       $05
+        Total frame CRC    5 * %0xxxxxxx
+
+   d - Tag restrictions
+
+     For some applications it might be desired to restrict a tag in more
+     ways than imposed by the ID3v2 specification. Note that the
+     presence of these restrictions does not affect how the tag is
+     decoded, merely how it was restricted before encoding. If this flag
+     is set the tag is restricted as follows:
+
+        Flag data length       $01
+        Restrictions           %ppqrrstt
+
+     p - Tag size restrictions
+
+       00   No more than 128 frames and 1 MB total tag size.
+       01   No more than 64 frames and 128 KB total tag size.
+       10   No more than 32 frames and 40 KB total tag size.
+       11   No more than 32 frames and 4 KB total tag size.
+
+     q - Text encoding restrictions
+
+       0    No restrictions
+       1    Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
+            UTF-8 [UTF-8].
+
+     r - Text fields size restrictions
+
+       00   No restrictions
+       01   No string is longer than 1024 characters.
+       10   No string is longer than 128 characters.
+       11   No string is longer than 30 characters.
+
+       Note that nothing is said about how many bytes is used to
+       represent those characters, since it is encoding dependent. If a
+       text frame consists of more than one string, the sum of the
+       strungs is restricted as stated.
+
+     s - Image encoding restrictions
+
+       0   No restrictions
+       1   Images are encoded only with PNG [PNG] or JPEG [JFIF].
+
+     t - Image size restrictions
+
+       00  No restrictions
+       01  All images are 256x256 pixels or smaller.
+       10  All images are 64x64 pixels or smaller.
+       11  All images are exactly 64x64 pixels, unless required
+           otherwise.
+
+
+3.3.   Padding
+
+   It is OPTIONAL to include padding after the final frame (at the end
+   of the ID3 tag), making the size of all the frames together smaller
+   than the size given in the tag header. A possible purpose of this
+   padding is to allow for adding a few additional frames or enlarge
+   existing frames within the tag without having to rewrite the entire
+   file. The value of the padding bytes must be $00. A tag MUST NOT have
+   any padding between the frames or between the tag header and the
+   frames. Furthermore it MUST NOT have any padding when a tag footer is
+   added to the tag.
+
+
+3.4.   ID3v2 footer
+
+   To speed up the process of locating an ID3v2 tag when searching from
+   the end of a file, a footer can be added to the tag. It is REQUIRED
+   to add a footer to an appended tag, i.e. a tag located after all
+   audio data. The footer is a copy of the header, but with a different
+   identifier.
+
+     ID3v2 identifier           "3DI"
+     ID3v2 version              $04 00
+     ID3v2 flags                %abcd0000
+     ID3v2 size             4 * %0xxxxxxx
+
+
+4.   ID3v2 frame overview
+
+   All ID3v2 frames consists of one frame header followed by one or more
+   fields containing the actual information. The header is always 10
+   bytes and laid out as follows:
+
+     Frame ID      $xx xx xx xx  (four characters)
+     Size      4 * %0xxxxxxx
+     Flags         $xx xx
+
+   The frame ID is made out of the characters capital A-Z and 0-9.
+   Identifiers beginning with "X", "Y" and "Z" are for experimental
+   frames and free for everyone to use, without the need to set the
+   experimental bit in the tag header. Bear in mind that someone else
+   might have used the same identifier as you. All other identifiers are
+   either used or reserved for future use.
+
+   The frame ID is followed by a size descriptor containing the size of
+   the data in the final frame, after encryption, compression and
+   unsynchronisation. The size is excluding the frame header ('total
+   frame size' - 10 bytes) and stored as a 32 bit synchsafe integer.
+
+   In the frame header the size descriptor is followed by two flag
+   bytes. These flags are described in section 4.1.
+
+   There is no fixed order of the frames' appearance in the tag,
+   although it is desired that the frames are arranged in order of
+   significance concerning the recognition of the file. An example of
+   such order: UFID, TIT2, MCDI, TRCK ...
+
+   A tag MUST contain at least one frame. A frame must be at least 1
+   byte big, excluding the header.
+
+   If nothing else is said, strings, including numeric strings and URLs
+   [URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the
+   range $20 - $FF. Such strings are represented in frame descriptions
+   as <text string>, or <full text string> if newlines are allowed. If
+   nothing else is said newline character is forbidden. In ISO-8859-1 a
+   newline is represented, when allowed, with $0A only.
+
+   Frames that allow different types of text encoding contains a text
+   encoding description byte. Possible encodings:
+
+     $00   ISO-8859-1 [ISO-8859-1]. Terminated with $00.
+     $01   UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All
+           strings in the same frame SHALL have the same byteorder.
+           Terminated with $00 00.
+     $02   UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
+           Terminated with $00 00.
+     $03   UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
+
+   Strings dependent on encoding are represented in frame descriptions
+   as <text string according to encoding>, or <full text string
+   according to encoding> if newlines are allowed. Any empty strings of
+   type $01 which are NULL-terminated may have the Unicode BOM followed
+   by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).
+
+   The timestamp fields are based on a subset of ISO 8601. When being as
+   precise as possible the format of a time string is
+   yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of
+   24), ":", minutes, ":", seconds), but the precision may be reduced by
+   removing as many time indicators as wanted. Hence valid timestamps
+   are
+   yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and
+   yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use
+   the slash character as described in 8601, and for multiple non-
+   contiguous dates, use multiple strings, if allowed by the frame
+   definition.
+
+   The three byte language field, present in several frames, is used to
+   describe the language of the frame's content, according to ISO-639-2
+   [ISO-639-2]. The language should be represented in lower case. If the
+   language is not known the string "XXX" should be used.
+
+   All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt".
+
+   If a frame is longer than it should be, e.g. having more fields than
+   specified in this document, that indicates that additions to the
+   frame have been made in a later version of the ID3v2 standard. This
+   is reflected by the revision number in the header of the tag.
+
+
+4.1.   Frame header flags
+
+   In the frame header the size descriptor is followed by two flag
+   bytes. All unused flags MUST be cleared. The first byte is for
+   'status messages' and the second byte is a format description. If an
+   unknown flag is set in the first byte the frame MUST NOT be changed
+   without that bit cleared. If an unknown flag is set in the second
+   byte the frame is likely to not be readable. Some flags in the second
+   byte indicates that extra information is added to the header. These
+   fields of extra information is ordered as the flags that indicates
+   them. The flags field is defined as follows (l and o left out because
+   ther resemblence to one and zero):
+
+     %0abc0000 %0h00kmnp
+
+   Some frame format flags indicate that additional information fields
+   are added to the frame. This information is added after the frame
+   header and before the frame data in the same order as the flags that
+   indicates them. I.e. the four bytes of decompressed size will precede
+   the encryption method byte. These additions affects the 'frame size'
+   field, but are not subject to encryption or compression.
+   
+   The default status flags setting for a frame is, unless stated
+   otherwise, 'preserved if tag is altered' and 'preserved if file is
+   altered', i.e. %00000000.
+
+
+4.1.1. Frame status flags
+
+   a - Tag alter preservation
+
+     This flag tells the tag parser what to do with this frame if it is
+     unknown and the tag is altered in any way. This applies to all
+     kinds of alterations, including adding more padding and reordering
+     the frames.
+
+     0     Frame should be preserved.
+     1     Frame should be discarded.
+
+
+   b - File alter preservation
+
+     This flag tells the tag parser what to do with this frame if it is
+     unknown and the file, excluding the tag, is altered. This does not
+     apply when the audio is completely replaced with other audio data.
+
+     0     Frame should be preserved.
+     1     Frame should be discarded.
+
+
+   c - Read only
+
+      This flag, if set, tells the software that the contents of this
+      frame are intended to be read only. Changing the contents might
+      break something, e.g. a signature. If the contents are changed,
+      without knowledge of why the frame was flagged read only and
+      without taking the proper means to compensate, e.g. recalculating
+      the signature, the bit MUST be cleared.
+
+
+4.1.2. Frame format flags
+
+   h - Grouping identity
+
+      This flag indicates whether or not this frame belongs in a group
+      with other frames. If set, a group identifier byte is added to the
+      frame. Every frame with the same group identifier belongs to the
+      same group.
+
+      0     Frame does not contain group information
+      1     Frame contains group information
+
+
+   k - Compression
+
+      This flag indicates whether or not the frame is compressed.
+      A 'Data Length Indicator' byte MUST be included in the frame.
+
+      0     Frame is not compressed.
+      1     Frame is compressed using zlib [zlib] deflate method.
+            If set, this requires the 'Data Length Indicator' bit
+            to be set as well.
+
+
+   m - Encryption
+   
+      This flag indicates whether or not the frame is encrypted. If set,
+      one byte indicating with which method it was encrypted will be
+      added to the frame. See description of the ENCR frame for more
+      information about encryption method registration. Encryption
+      should be done after compression. Whether or not setting this flag
+      requires the presence of a 'Data Length Indicator' depends on the
+      specific algorithm used.
+
+      0     Frame is not encrypted.
+      1     Frame is encrypted.
+
+   n - Unsynchronisation
+
+      This flag indicates whether or not unsynchronisation was applied
+      to this frame. See section 6 for details on unsynchronisation.
+      If this flag is set all data from the end of this header to the
+      end of this frame has been unsynchronised. Although desirable, the
+      presence of a 'Data Length Indicator' is not made mandatory by
+      unsynchronisation.
+
+      0     Frame has not been unsynchronised.
+      1     Frame has been unsyrchronised.
+
+   p - Data length indicator
+
+      This flag indicates that a data length indicator has been added to
+      the frame. The data length indicator is the value one would write
+      as the 'Frame length' if all of the frame format flags were
+      zeroed, represented as a 32 bit synchsafe integer.
+
+      0      There is no Data Length Indicator.
+      1      A data length Indicator has been added to the frame.
+
+
+5.   Tag location
+
+   The default location of an ID3v2 tag is prepended to the audio so
+   that players can benefit from the information when the data is
+   streamed. It is however possible to append the tag, or make a
+   prepend/append combination. When deciding upon where an unembedded
+   tag should be located, the following order of preference SHOULD be
+   considered.
+   
+     1. Prepend the tag.
+
+     2. Prepend a tag with all vital information and add a second tag at 
+        the end of the file, before tags from other tagging systems. The
+        first tag is required to have a SEEK frame.
+      
+     3. Add a tag at the end of the file, before tags from other tagging
+        systems.
+      
+   In case 2 and 3 the tag can simply be appended if no other known tags
+   are present. The suggested method to find ID3v2 tags are:
+   
+     1. Look for a prepended tag using the pattern found in section 3.1.
+
+     2. If a SEEK frame was found, use its values to guide further
+        searching.
+
+     3. Look for a tag footer, scanning from the back of the file.
+
+   For every new tag that is found, the old tag should be discarded
+   unless the update flag in the extended header (section 3.2) is set.
+   
+
+6.   Unsynchronisation
+
+   The only purpose of unsynchronisation is to make the ID3v2 tag as
+   compatible as possible with existing software and hardware. There is
+   no use in 'unsynchronising' tags if the file is only to be processed
+   only by ID3v2 aware software and hardware. Unsynchronisation is only
+   useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC
+   files.
+
+
+6.1.   The unsynchronisation scheme
+
+   Whenever a false synchronisation is found within the tag, one zeroed
+   byte is inserted after the first false synchronisation byte. The
+   format of synchronisations that should be altered by ID3 encoders is
+   as follows:
+
+         %11111111 111xxxxx
+
+   and should be replaced with:
+
+         %11111111 00000000 111xxxxx
+
+   This has the side effect that all $FF 00 combinations have to be
+   altered, so they will not be affected by the decoding process.
+   Therefore all the $FF 00 combinations have to be replaced with the
+   $FF 00 00 combination during the unsynchronisation.
+
+   To indicate usage of the unsynchronisation, the unsynchronisation
+   flag in the frame header should be set. This bit MUST be set if the
+   frame was altered by the unsynchronisation and SHOULD NOT be set if
+   unaltered. If all frames in the tag are unsynchronised the
+   unsynchronisation flag in the tag header SHOULD be set. It MUST NOT
+   be set if the tag has a frame which is not unsynchronised.
+
+   Assume the first byte of the audio to be $FF. The special case when
+   the last byte of the last frame is $FF and no padding nor footer is
+   used will then introduce a false synchronisation. This can be solved
+   by adding a footer, adding padding or unsynchronising the frame and
+   add $00 to the end of the frame data, thus adding more byte to the
+   frame size than a normal unsynchronisation would. Although not
+   preferred, it is allowed to apply the last method on all frames
+   ending with $FF.
+
+   It is preferred that the tag is either completely unsynchronised or
+   not unsynchronised at all. A completely unsynchronised tag has no 
+   false synchonisations in it, as defined above, and does not end with
+   $FF. A completely non-unsynchronised tag contains no unsynchronised
+   frames, and thus the unsynchronisation flag in the header is cleared.
+
+   Do bear in mind, that if compression or encryption is used, the
+   unsynchronisation scheme MUST be applied afterwards. When decoding an
+   unsynchronised frame, the unsynchronisation scheme MUST be reversed
+   first, encryption and decompression afterwards.
+
+
+6.2.   Synchsafe integers
+
+   In some parts of the tag it is inconvenient to use the
+   unsychronisation scheme because the size of unsynchronised data is
+   not known in advance, which is particularly problematic with size
+   descriptors. The solution in ID3v2 is to use synchsafe integers, in
+   which there can never be any false synchs. Synchsafe integers are
+   integers that keep its highest bit (bit 7) zeroed, making seven bits
+   out of eight available. Thus a 32 bit synchsafe integer can store 28
+   bits of information.
+   
+   Example:
+   
+     255 (%11111111) encoded as a 16 bit synchsafe integer is 383
+     (%00000001 01111111).
+
+
+7.   Copyright
+
+   Copyright (C) Martin Nilsson 2000. All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that a reference to this document is included on all
+   such copies and derivative works. However, this document itself may
+   not be modified in any way and reissued as the original document.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked.
+
+   This document and the information contained herein is provided on an
+   'AS IS' basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+   THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+   WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+8.   References
+
+   [ID3v2] Martin Nilsson, 'ID3v2 informal standard'.
+
+      <url:http://www.id3.org/id3v2.3.0.txt>
+
+   [ISO-639-2] ISO/FDIS 639-2.
+   'Codes for the representation of names of languages, Part 2: Alpha-3
+   code.' Technical committee / subcommittee: TC 37 / SC 2
+
+   [ISO-3309] ISO 3309
+   'Information Processing Systems--Data Communication High-Level Data
+   Link Control Procedure--Frame Structure', IS 3309, October 1984, 3rd
+   Edition.
+
+   [ISO-8859-1] ISO/IEC DIS 8859-1.
+   '8-bit single-byte coded graphic character sets, Part 1: Latin
+   alphabet No. 1.' Technical committee / subcommittee: JTC 1 / SC 2
+
+   [JFIF] 'JPEG File Interchange Format, version 1.02'
+
+      <url:http://www.w3.org/Graphics/JPEG/jfif.txt>
+
+   [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate
+   Requirement Levels', RFC 2119, March 1997.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc2119.txt>
+
+   [MPEG] ISO/IEC 11172-3:1993.
+   'Coding of moving pictures and associated audio for digital storage
+   media at up to about 1,5 Mbit/s, Part 3: Audio.'
+   Technical committee / subcommittee: JTC 1 / SC 29
+    and
+   ISO/IEC 13818-3:1995
+   'Generic coding of moving pictures and associated audio information,
+   Part 3: Audio.'
+   Technical committee / subcommittee: JTC 1 / SC 29
+    and
+   ISO/IEC DIS 13818-3
+   'Generic coding of moving pictures and associated audio information,
+   Part 3: Audio (Revision of ISO/IEC 13818-3:1995)'
+
+   [PNG] 'Portable Network Graphics, version 1.0'
+
+      <url:http://www.w3.org/TR/REC-png-multi.html>
+
+   [UNICODE] The Unicode Consortium,
+   'The Unicode Standard Version 3.0', ISBN 0-201-61633-5.
+
+   <url:http://www.unicode.org/unicode/standard/versions/Unicode3.0.htm>
+
+   [URL] T. Berners-Lee, L. Masinter & M. McCahill, 'Uniform Resource
+   Locators (URL)', RFC 1738, December 1994.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
+
+   [UTF-8] F. Yergeau, 'UTF-8, a transformation format of ISO 10646',
+   RFC 2279, January 1998.
+   
+      <url:ftp://ftp.isi.edu/in-notes/rfc2279.txt>
+
+   [UTF-16] F. Yergeau, 'UTF-16, an encoding of ISO 10646', RFC 2781,
+   February 2000.
+   
+      <url:ftp://ftp.isi.edu/in-notes/rfc2781.txt>
+   
+   [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, 'ZLIB
+   Compressed Data Format Specification version 3.3', RFC 1950,
+   May 1996.
+
+      <url:ftp://ftp.isi.edu/in-notes/rfc1950.txt>
+
+
+9.   Author's Address
+
+   Written by
+
+     Martin Nilsson
+     Rydsvägen 246 C. 30
+     SE-584 34 Linköping
+     Sweden
+
+     Email: nilsson@id3.org
+
diff --git a/src/taglib/mpeg/id3v2/id3v2extendedheader.cpp b/src/taglib/mpeg/id3v2/id3v2extendedheader.cpp
new file mode 100644 (file)
index 0000000..bd470a0
--- /dev/null
@@ -0,0 +1,71 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "id3v2extendedheader.h"
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class ExtendedHeader::ExtendedHeaderPrivate
+{
+public:
+  ExtendedHeaderPrivate() : size(0) {}
+
+  uint size;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+ExtendedHeader::ExtendedHeader()
+{
+  d = new ExtendedHeaderPrivate();
+}
+
+ExtendedHeader::~ExtendedHeader()
+{
+  delete d;
+}
+
+TagLib::uint ExtendedHeader::size() const
+{
+  return d->size;
+}
+
+void ExtendedHeader::setData(const ByteVector &data)
+{
+  parse(data);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void ExtendedHeader::parse(const ByteVector &data)
+{
+  d->size = SynchData::toUInt(data.mid(0, 4)); // (structure 3.2 "Extended header size")
+}
diff --git a/src/taglib/mpeg/id3v2/id3v2extendedheader.h b/src/taglib/mpeg/id3v2/id3v2extendedheader.h
new file mode 100644 (file)
index 0000000..9750fae
--- /dev/null
@@ -0,0 +1,93 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V2EXTENDEDHEADER_H
+#define TAGLIB_ID3V2EXTENDEDHEADER_H
+
+#include "taglib_export.h"
+#include "tbytevector.h"
+#include "taglib.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! ID3v2 extended header implementation
+
+    /*!
+     * This class implements ID3v2 extended headers.  It attempts to follow,
+     * both  semantically and programatically, the structure specified in
+     * the ID3v2 standard.  The API is based on the properties of ID3v2 extended
+     * headers specified there.  If any of the terms used in this documentation
+     * are unclear please check the specification in the linked section.
+     * (Structure, <a href="id3v2-structure.html#3.2">3.2</a>)
+     */
+
+    class TAGLIB_EXPORT ExtendedHeader
+    {
+    public:
+      /*!
+       * Constructs an empty ID3v2 extended header.
+       */
+      ExtendedHeader();
+
+      /*!
+       * Destroys the extended header.
+       */
+      virtual ~ExtendedHeader();
+
+      /*!
+       * Returns the size of the extended header.  This is variable for the
+       * extended header.
+       */
+      uint size() const;
+
+      /*!
+       * Sets the data that will be used as the extended header.  Since the
+       * length is not known before the extended header has been parsed, this
+       * should just be a pointer to the first byte of the extended header.  It
+       * will determine the length internally and make that available through
+       * size().
+       */
+      void setData(const ByteVector &data);
+
+    protected:
+      /*!
+       * Called by setData() to parse the extended header data.  It makes this
+       * information available through the public API.
+       */
+      void parse(const ByteVector &data);
+
+    private:
+      ExtendedHeader(const ExtendedHeader &);
+      ExtendedHeader &operator=(const ExtendedHeader &);
+
+      class ExtendedHeaderPrivate;
+      ExtendedHeaderPrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/id3v2footer.cpp b/src/taglib/mpeg/id3v2/id3v2footer.cpp
new file mode 100644 (file)
index 0000000..d542433
--- /dev/null
@@ -0,0 +1,60 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "id3v2footer.h"
+#include "id3v2header.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class Footer::FooterPrivate
+{
+public:
+  static const uint size = 10;
+};
+
+Footer::Footer()
+{
+
+}
+
+Footer::~Footer()
+{
+
+}
+
+TagLib::uint Footer::size()
+{
+  return FooterPrivate::size;
+}
+
+ByteVector Footer::render(const Header *header) const
+{
+    ByteVector headerData = header->render();
+    headerData[0] = '3';
+    headerData[1] = 'D';
+    headerData[2] = 'I';
+    return headerData;
+}
diff --git a/src/taglib/mpeg/id3v2/id3v2footer.h b/src/taglib/mpeg/id3v2/id3v2footer.h
new file mode 100644 (file)
index 0000000..5d0e1af
--- /dev/null
@@ -0,0 +1,82 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V2FOOTER_H
+#define TAGLIB_ID3V2FOOTER_H
+
+#include "taglib_export.h"
+#include "tbytevector.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    class Header;
+
+    //! ID3v2 footer implementation
+
+    /*!
+     * Per the ID3v2 specification, the tag's footer is just a copy of the
+     * information in the header.  As such there is no API for reading the
+     * data from the header, it can just as easily be done from the header.
+     *
+     * In fact, at this point, TagLib does not even parse the footer since
+     * it is not useful internally.  However, if the flag to include a footer
+     * has been set in the ID3v2::Tag, TagLib will render a footer.
+     */
+
+    class TAGLIB_EXPORT Footer
+    {
+    public:
+      /*!
+       * Constructs an empty ID3v2 footer.
+       */
+      Footer();
+      /*!
+       * Destroys the footer.
+       */
+      virtual ~Footer();
+
+      /*!
+       * Returns the size of the footer.  Presently this is always 10 bytes.
+       */
+      static uint size();
+
+      /*!
+       * Renders the footer based on the data in \a header.
+       */
+      ByteVector render(const Header *header) const;
+
+    private:
+      Footer(const Footer &);
+      Footer &operator=(const Footer &);
+
+      class FooterPrivate;
+      FooterPrivate *d;
+    };
+
+  }
+}
+#endif
diff --git a/src/taglib/mpeg/id3v2/id3v2frame.cpp b/src/taglib/mpeg/id3v2/id3v2frame.cpp
new file mode 100644 (file)
index 0000000..a6f946b
--- /dev/null
@@ -0,0 +1,551 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#define HAVE_ZLIB 0
+
+#ifndef HAVE_ZLIB
+#include <config.h>
+#endif
+
+#if HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#include <bitset>
+
+#include <tdebug.h>
+#include <tstringlist.h>
+
+#include "id3v2frame.h"
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class Frame::FramePrivate
+{
+public:
+  FramePrivate() :
+    header(0)
+    {}
+
+  ~FramePrivate()
+  {
+    delete header;
+  }
+
+  Frame::Header *header;
+};
+
+namespace
+{
+  bool isValidFrameID(const ByteVector &frameID)
+  {
+    if(frameID.size() != 4)
+      return false;
+
+    for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
+      if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static methods
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Frame::headerSize()
+{
+  return Header::size();
+}
+
+TagLib::uint Frame::headerSize(uint version)
+{
+  return Header::size(version);
+}
+
+ByteVector Frame::textDelimiter(String::Type t)
+{
+  ByteVector d = char(0);
+  if(t == String::UTF16 || t == String::UTF16BE || t == String::UTF16LE)
+    d.append(char(0));
+  return d;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::~Frame()
+{
+  delete d;
+}
+
+ByteVector Frame::frameID() const
+{
+  if(d->header)
+    return d->header->frameID();
+  else
+    return ByteVector::null;
+}
+
+TagLib::uint Frame::size() const
+{
+  if(d->header)
+    return d->header->frameSize();
+  else
+    return 0;
+}
+
+void Frame::setData(const ByteVector &data)
+{
+  parse(data);
+}
+
+void Frame::setText(const String &)
+{
+
+}
+
+ByteVector Frame::render() const
+{
+  ByteVector fieldData = renderFields();
+  d->header->setFrameSize(fieldData.size());
+  ByteVector headerData = d->header->render();
+
+  return headerData + fieldData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::Frame(const ByteVector &data)
+{
+  d = new FramePrivate;
+  d->header = new Header(data);
+}
+
+Frame::Frame(Header *h)
+{
+  d = new FramePrivate;
+  d->header = h;
+}
+
+Frame::Header *Frame::header() const
+{
+  return d->header;
+}
+
+void Frame::setHeader(Header *h, bool deleteCurrent)
+{
+  if(deleteCurrent)
+    delete d->header;
+
+  d->header = h;
+}
+
+void Frame::parse(const ByteVector &data)
+{
+  if(d->header)
+    d->header->setData(data);
+  else
+    d->header = new Header(data);
+
+  parseFields(fieldData(data));
+}
+
+ByteVector Frame::fieldData(const ByteVector &frameData) const
+{
+  uint headerSize = Header::size(d->header->version());
+
+  uint frameDataOffset = headerSize;
+  uint frameDataLength = size();
+
+  if(d->header->compression() || d->header->dataLengthIndicator()) {
+    frameDataLength = SynchData::toUInt(frameData.mid(headerSize, 4));
+    frameDataOffset += 4;
+  }
+
+#if HAVE_ZLIB
+  if(d->header->compression() &&
+     !d->header->encryption())
+  {
+    ByteVector data(frameDataLength);
+    uLongf uLongTmp = frameDataLength;
+    ::uncompress((Bytef *) data.data(),
+                 (uLongf *) &uLongTmp,
+                 (Bytef *) frameData.data() + frameDataOffset,
+                 size());
+    return data;
+  }
+  else
+#endif
+    return frameData.mid(frameDataOffset, frameDataLength);
+}
+
+String Frame::readStringField(const ByteVector &data, String::Type encoding, int *position)
+{
+  int start = 0;
+
+  if(!position)
+    position = &start;
+
+  ByteVector delimiter = textDelimiter(encoding);
+
+  int end = data.find(delimiter, *position, delimiter.size());
+
+  if(end < *position)
+    return String::null;
+
+  String str = String(data.mid(*position, end - *position), encoding);
+
+  *position = end + delimiter.size();
+
+  return str;
+}
+
+String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static
+{
+  if(encoding != String::Latin1)
+    return encoding;
+
+  for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
+    if(!(*it).isLatin1()) {
+      debug("Frame::checkEncoding() -- Rendering using UTF8.");
+      return String::UTF8;
+    }
+  }
+
+  return String::Latin1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Frame::Header class
+////////////////////////////////////////////////////////////////////////////////
+
+class Frame::Header::HeaderPrivate
+{
+public:
+  HeaderPrivate() :
+    frameSize(0),
+    version(4),
+    tagAlterPreservation(false),
+    fileAlterPreservation(false),
+    readOnly(false),
+    groupingIdentity(false),
+    compression(false),
+    encryption(false),
+    unsynchronisation(false),
+    dataLengthIndicator(false)
+    {}
+
+  ByteVector frameID;
+  uint frameSize;
+  uint version;
+
+  // flags
+
+  bool tagAlterPreservation;
+  bool fileAlterPreservation;
+  bool readOnly;
+  bool groupingIdentity;
+  bool compression;
+  bool encryption;
+  bool unsynchronisation;
+  bool dataLengthIndicator;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members (Frame::Header)
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Frame::Header::size()
+{
+  return size(4);
+}
+
+TagLib::uint Frame::Header::size(uint version)
+{
+  switch(version) {
+  case 0:
+  case 1:
+  case 2:
+    return 6;
+  case 3:
+  case 4:
+  default:
+    return 10;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members (Frame::Header)
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::Header::Header(const ByteVector &data, bool synchSafeInts)
+{
+  d = new HeaderPrivate;
+  setData(data, synchSafeInts);
+}
+
+Frame::Header::Header(const ByteVector &data, uint version)
+{
+  d = new HeaderPrivate;
+  setData(data, version);
+}
+
+Frame::Header::~Header()
+{
+  delete d;
+}
+
+void Frame::Header::setData(const ByteVector &data, bool synchSafeInts)
+{
+  setData(data, uint(synchSafeInts ? 4 : 3));
+}
+
+void Frame::Header::setData(const ByteVector &data, uint version)
+{
+  d->version = version;
+
+  switch(version) {
+  case 0:
+  case 1:
+  case 2:
+  {
+    // ID3v2.2
+
+    if(data.size() < 3) {
+      debug("You must at least specify a frame ID.");
+      return;
+    }
+
+    // Set the frame ID -- the first three bytes
+
+    d->frameID = data.mid(0, 3);
+
+    // If the full header information was not passed in, do not continue to the
+    // steps to parse the frame size and flags.
+
+    if(data.size() < 6) {
+      d->frameSize = 0;
+      return;
+    }
+
+    d->frameSize = data.mid(3, 3).toUInt();
+
+    break;
+  }
+  case 3:
+  {
+    // ID3v2.3
+
+    if(data.size() < 4) {
+      debug("You must at least specify a frame ID.");
+      return;
+    }
+
+    // Set the frame ID -- the first four bytes
+
+    d->frameID = data.mid(0, 4);
+
+    // If the full header information was not passed in, do not continue to the
+    // steps to parse the frame size and flags.
+
+    if(data.size() < 10) {
+      d->frameSize = 0;
+      return;
+    }
+
+    // Set the size -- the frame size is the four bytes starting at byte four in
+    // the frame header (structure 4)
+
+    d->frameSize = data.mid(4, 4).toUInt();
+
+    { // read the first byte of flags
+      std::bitset<8> flags(data[8]);
+      d->tagAlterPreservation  = flags[7]; // (structure 3.3.1.a)
+      d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b)
+      d->readOnly              = flags[5]; // (structure 3.3.1.c)
+    }
+
+    { // read the second byte of flags
+      std::bitset<8> flags(data[9]);
+      d->compression         = flags[7]; // (structure 3.3.1.i)
+      d->encryption          = flags[6]; // (structure 3.3.1.j)
+      d->groupingIdentity    = flags[5]; // (structure 3.3.1.k)
+    }
+    break;
+  }
+  case 4:
+  default:
+  {
+    // ID3v2.4
+
+    if(data.size() < 4) {
+      debug("You must at least specify a frame ID.");
+      return;
+    }
+
+    // Set the frame ID -- the first four bytes
+
+    d->frameID = data.mid(0, 4);
+
+    // If the full header information was not passed in, do not continue to the
+    // steps to parse the frame size and flags.
+
+    if(data.size() < 10) {
+      d->frameSize = 0;
+      return;
+    }
+
+    // Set the size -- the frame size is the four bytes starting at byte four in
+    // the frame header (structure 4)
+
+    d->frameSize = SynchData::toUInt(data.mid(4, 4));
+#ifndef NO_ITUNES_HACKS
+    // iTunes writes v2.4 tags with v2.3-like frame sizes
+    if(d->frameSize > 127) {
+      if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
+        unsigned int uintSize = data.mid(4, 4).toUInt();
+        if(isValidFrameID(data.mid(uintSize + 10, 4))) {
+          d->frameSize = uintSize;
+        }
+      }
+    }
+#endif
+
+    { // read the first byte of flags
+      std::bitset<8> flags(data[8]);
+      d->tagAlterPreservation  = flags[6]; // (structure 4.1.1.a)
+      d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b)
+      d->readOnly              = flags[4]; // (structure 4.1.1.c)
+    }
+
+    { // read the second byte of flags
+      std::bitset<8> flags(data[9]);
+      d->groupingIdentity    = flags[6]; // (structure 4.1.2.h)
+      d->compression         = flags[3]; // (structure 4.1.2.k)
+      d->encryption          = flags[2]; // (structure 4.1.2.m)
+      d->unsynchronisation   = flags[1]; // (structure 4.1.2.n)
+      d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p)
+    }
+    break;
+  }
+  }
+}
+
+ByteVector Frame::Header::frameID() const
+{
+  return d->frameID;
+}
+
+void Frame::Header::setFrameID(const ByteVector &id)
+{
+  d->frameID = id.mid(0, 4);
+}
+
+TagLib::uint Frame::Header::frameSize() const
+{
+  return d->frameSize;
+}
+
+void Frame::Header::setFrameSize(uint size)
+{
+  d->frameSize = size;
+}
+
+TagLib::uint Frame::Header::version() const
+{
+  return d->version;
+}
+
+bool Frame::Header::tagAlterPreservation() const
+{
+  return d->tagAlterPreservation;
+}
+
+void Frame::Header::setTagAlterPreservation(bool preserve)
+{
+  d->tagAlterPreservation = preserve;
+}
+
+bool Frame::Header::fileAlterPreservation() const
+{
+  return d->fileAlterPreservation;
+}
+
+bool Frame::Header::readOnly() const
+{
+  return d->readOnly;
+}
+
+bool Frame::Header::groupingIdentity() const
+{
+  return d->groupingIdentity;
+}
+
+bool Frame::Header::compression() const
+{
+  return d->compression;
+}
+
+bool Frame::Header::encryption() const
+{
+  return d->encryption;
+}
+
+bool Frame::Header::unsycronisation() const
+{
+  return unsynchronisation();
+}
+
+bool Frame::Header::unsynchronisation() const
+{
+  return d->unsynchronisation;
+}
+
+bool Frame::Header::dataLengthIndicator() const
+{
+  return d->dataLengthIndicator;
+}
+
+ByteVector Frame::Header::render() const
+{
+  ByteVector flags(2, char(0)); // just blank for the moment
+
+  ByteVector v = d->frameID + SynchData::fromUInt(d->frameSize) + flags;
+
+  return v;
+}
+
+bool Frame::Header::frameAlterPreservation() const
+{
+  return fileAlterPreservation();
+}
diff --git a/src/taglib/mpeg/id3v2/id3v2frame.h b/src/taglib/mpeg/id3v2/id3v2frame.h
new file mode 100644 (file)
index 0000000..e76d060
--- /dev/null
@@ -0,0 +1,414 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V2FRAME_H
+#define TAGLIB_ID3V2FRAME_H
+
+#include "tstring.h"
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  class StringList;
+
+  namespace ID3v2 {
+
+    class Tag;
+    class FrameFactory;
+
+    //! ID3v2 frame implementation
+
+    /*!
+     * This class is the main ID3v2 frame implementation.  In ID3v2, a tag is
+     * split between a collection of frames (which are in turn split into fields
+     * (Structure, <a href="id3v2-structure.html#4">4</a>)
+     * (<a href="id3v2-frames.html">Frames</a>).  This class provides an API for
+     * gathering information about and modifying ID3v2 frames.  Funtionallity
+     * specific to a given frame type is handed in one of the many subclasses.
+     */
+
+    class TAGLIB_EXPORT Frame
+    {
+      friend class Tag;
+      friend class FrameFactory;
+
+    public:
+      /*!
+       * Destroys this Frame instance.
+       */
+      virtual ~Frame();
+
+      /*!
+       * Returns the Frame ID (Structure, <a href="id3v2-structure.html#4">4</a>)
+       * (Frames, <a href="id3v2-frames.html#4">4</a>)
+       */
+      ByteVector frameID() const;
+
+      /*!
+       * Returns the size of the frame.
+       */
+      uint size() const;
+
+      /*!
+       * Returns the size of the frame header
+       *
+       * \deprecated This is only accurate for ID3v2.3 or ID3v2.4.  Please use
+       * the call below which accepts an ID3v2 version number.  In the next
+       * non-binary compatible release this will be made into a non-static
+       * member that checks the internal ID3v2 version.
+       */
+      static uint headerSize(); // BIC: remove and make non-static
+
+      /*!
+       * Returns the size of the frame header for the given ID3v2 version.
+       *
+       * \deprecated Please see the explanation above.
+       */
+      static uint headerSize(uint version); // BIC: remove and make non-static
+
+      /*!
+       * Sets the data that will be used as the frame.  Since the length is not
+       * known before the frame has been parsed, this should just be a pointer to
+       * the first byte of the frame.  It will determine the length internally
+       * and make that available through size().
+       */
+      void setData(const ByteVector &data);
+
+      /*!
+       * Set the text of frame in the sanest way possible.  This should only be
+       * reimplemented in frames where there is some logical mapping to text.
+       *
+       * \note If the frame type supports multiple text encodings, this will not
+       * change the text encoding of the frame; the string will be converted to
+       * that frame's encoding.  Please use the specific APIs of the frame types
+       * to set the encoding if that is desired.
+       */
+      virtual void setText(const String &text);
+
+      /*!
+       * This returns the textual representation of the data in the frame.
+       * Subclasses must reimplement this method to provide a string
+       * representation of the frame's data.
+       */
+      virtual String toString() const = 0;
+
+      /*!
+       * Render the frame back to its binary format in a ByteVector.
+       */
+      ByteVector render() const;
+
+      /*!
+       * Returns the text delimiter that is used between fields for the string
+       * type \a t.
+       */
+      static ByteVector textDelimiter(String::Type t);
+
+    protected:
+      class Header;
+
+      /*!
+       * Constructs an ID3v2 frame using \a data to read the header information.
+       * All other processing of \a data should be handled in a subclass.
+       *
+       * \note This need not contain anything more than a frame ID, but
+       * \e must constain at least that.
+       */
+      explicit Frame(const ByteVector &data);
+
+      /*!
+       * This creates an Frame using the header \a h.
+       *
+       * The ownership of this header will be assigned to the frame and the
+       * header will be deleted when the frame is destroyed.
+       */
+      Frame(Header *h);
+
+      /*!
+       * Returns a pointer to the frame header.
+       */
+      Header *header() const;
+
+      /*!
+       * Sets the header to \a h.  If \a deleteCurrent is true, this will free
+       * the memory of the current header.
+       *
+       * The ownership of this header will be assigned to the frame and the
+       * header will be deleted when the frame is destroyed.
+       */
+      void setHeader(Header *h, bool deleteCurrent = true);
+
+      /*!
+       * Called by setData() to parse the frame data.  It makes this information
+       * available through the public API.
+       */
+      void parse(const ByteVector &data);
+
+      /*!
+       * Called by parse() to parse the field data.  It makes this information
+       * available through the public API.  This must be overridden by the
+       * subclasses.
+       */
+      virtual void parseFields(const ByteVector &data) = 0;
+
+      /*!
+       * Render the field data back to a binary format in a ByteVector.  This
+       * must be overridden by subclasses.
+       */
+      virtual ByteVector renderFields() const = 0;
+
+      /*!
+       * Returns a ByteVector containing the field data given the frame data.
+       * This correctly adjusts for the header size plus any additional frame
+       * data that's specified in the frame header flags.
+       */
+      ByteVector fieldData(const ByteVector &frameData) const;
+
+      /*!
+       * Reads a String of type \a encodiong from the ByteVector \a data.  If \a
+       * position is passed in it is used both as the starting point and is
+       * updated to replect the position just after the string that has been read.
+       * This is useful for reading strings sequentially.
+       */
+      String readStringField(const ByteVector &data, String::Type encoding,
+                             int *positon = 0);
+
+      /*!
+       * Checks a the list of string values to see if they can be used with the
+       * specified encoding and returns the recommended encoding.
+       */
+      static String::Type checkEncoding(const StringList &fields,
+                                        String::Type encoding);
+
+    private:
+      Frame(const Frame &);
+      Frame &operator=(const Frame &);
+
+      class FramePrivate;
+      friend class FramePrivate;
+      FramePrivate *d;
+    };
+
+    //! ID3v2 frame header implementation
+
+    /*!
+     * The ID3v2 Frame Header (Structure, <a href="id3v2-structure.html#4">4</a>)
+     *
+     * Every ID3v2::Frame has an associated header that gives some general
+     * properties of the frame and also makes it possible to identify the frame
+     * type.
+     *
+     * As such when reading an ID3v2 tag ID3v2::FrameFactory first creates the
+     * frame headers and then creates the appropriate Frame subclass based on
+     * the type and attaches the header.
+     */
+
+    class TAGLIB_EXPORT Frame::Header
+    {
+    public:
+      /*!
+       * Construct a Frame Header based on \a data.  \a data must at least
+       * contain a 4 byte frame ID, and optionally can contain flag data and the
+       * frame size.  i.e. Just the frame id -- "TALB" -- is a valid value.
+       *
+       * \deprecated Please use the constructor below that accepts a version
+       * number.
+       */
+      Header(const ByteVector &data, bool synchSafeInts);
+
+      /*!
+       * Construct a Frame Header based on \a data.  \a data must at least
+       * contain a 4 byte frame ID, and optionally can contain flag data and the
+       * frame size.  i.e. Just the frame id -- "TALB" -- is a valid value.
+       *
+       * \a version should be the ID3v2 version of the tag.
+       */
+      explicit Header(const ByteVector &data, uint version = 4);
+
+      /*!
+       * Destroys this Header instance.
+       */
+      virtual ~Header();
+
+      /*!
+       * Sets the data for the Header.
+       *
+       * \deprecated Please use the version below that accepts an ID3v2 version
+       * number.
+       */
+      void setData(const ByteVector &data, bool synchSafeInts);
+
+      /*!
+       * Sets the data for the Header.  \a version should indicate the ID3v2
+       * version number of the tag that this frame is contained in.
+       */
+      void setData(const ByteVector &data, uint version = 4);
+
+      /*!
+       * Returns the Frame ID (Structure, <a href="id3v2-structure.html#4">4</a>)
+       * (Frames, <a href="id3v2-frames.html#4">4</a>)
+       */
+      ByteVector frameID() const;
+
+      /*!
+       * Sets the frame's ID to \a id.  Only the first four bytes of \a id will
+       * be used.
+       *
+       * \warning This method should in general be avoided.  It exists simply to
+       * provide a mechanism for transforming frames from a deprecated frame type
+       * to a newer one -- i.e. TYER to TDRC from ID3v2.3 to ID3v2.4.
+       */
+      void setFrameID(const ByteVector &id);
+
+      /*!
+       * Returns the size of the frame data portion, as set when setData() was
+       * called or set explicitly via setFrameSize().
+       */
+      uint frameSize() const;
+
+      /*!
+       * Sets the size of the frame data portion.
+       */
+      void setFrameSize(uint size);
+
+      /*!
+       * Returns the ID3v2 version of the header (as passed in from the
+       * construction of the header).
+       */
+      uint version() const;
+
+      /*!
+       * Returns the size of the frame header in bytes.
+       *
+       * \deprecated Please use the version of this method that accepts a
+       * version.  This is only accurate for ID3v2.3 and ID3v2.4.  This will be
+       * removed in the next binary incompatible release (2.0) and will be
+       * replaced with a non-static method that checks the frame version.
+       */
+      static uint size();
+
+      /*!
+       * Returns the size of the frame header in bytes for the ID3v2 version
+       * that's given.
+       *
+       * \deprecated Please see the explanation in the version above.
+       */
+      static uint size(uint version);
+
+      /*!
+       * Returns true if the flag for tag alter preservation is set.
+       *
+       * The semantics are a little backwards from what would seem natural
+       * (setting the preservation flag to throw away the frame), but this
+       * follows the ID3v2 standard.
+       *
+       * \see setTagAlterPreservation()
+       */
+      bool tagAlterPreservation() const;
+
+      /*!
+       * Sets the flag for preservation of this frame if the tag is set.  If
+       * this is set to true the frame will not be written when the tag is
+       * saved.
+       *
+       * The semantics are a little backwards from what would seem natural
+       * (setting the preservation flag to throw away the frame), but this
+       * follows the ID3v2 standard.
+       *
+       * \see tagAlterPreservation()
+       */
+      void setTagAlterPreservation(bool discard);
+
+      /*!
+       * Returns true if the flag for file alter preservation is set.
+       *
+       * \note This flag is currently ignored internally in TagLib.
+       */
+      bool fileAlterPreservation() const;
+
+      /*!
+       * Returns true if the frame is meant to be read only.
+       *
+       * \note This flag is currently ignored internally in TagLib.
+       */
+      bool readOnly() const;
+
+      /*!
+       * Returns true if the flag for the grouping identifity is set.
+       *
+       * \note This flag is currently ignored internally in TagLib.
+       */
+      bool groupingIdentity() const;
+
+      /*!
+       * Returns true if compression is enabled for this frame.
+       *
+       * \note This flag is currently ignored internally in TagLib.
+       */
+      bool compression() const;
+
+      /*!
+       * Returns true if encryption is enabled for this frame.
+       *
+       * \note This flag is currently ignored internally in TagLib.
+       */
+      bool encryption() const;
+
+#ifndef DO_NOT_DOCUMENT
+      bool unsycronisation() const;
+#endif
+
+      /*!
+       * Returns true if unsynchronisation is enabled for this frame.
+       */
+      bool unsynchronisation() const;
+
+      /*!
+       * Returns true if the flag for a data length indicator is set.
+       */
+      bool dataLengthIndicator() const;
+
+      /*!
+       * Render the Header back to binary format in a ByteVector.
+       */
+      ByteVector render() const;
+
+      /*!
+       * \deprecated
+       */
+      bool frameAlterPreservation() const;
+
+    private:
+      Header(const Header &);
+      Header &operator=(const Header &);
+
+      class HeaderPrivate;
+      HeaderPrivate *d;
+    };
+
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/id3v2framefactory.cpp b/src/taglib/mpeg/id3v2/id3v2framefactory.cpp
new file mode 100644 (file)
index 0000000..273baf5
--- /dev/null
@@ -0,0 +1,435 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#define HAVE_ZLIB 0
+
+#ifndef HAVE_ZLIB
+#include <config.h>
+#endif
+
+#include <tdebug.h>
+
+#include "id3v2framefactory.h"
+#include "id3v2synchdata.h"
+#include "id3v1genres.h"
+
+#include "frames/attachedpictureframe.h"
+#include "frames/commentsframe.h"
+#include "frames/relativevolumeframe.h"
+#include "frames/textidentificationframe.h"
+#include "frames/uniquefileidentifierframe.h"
+#include "frames/unknownframe.h"
+#include "frames/generalencapsulatedobjectframe.h"
+#include "frames/urllinkframe.h"
+#include "frames/unsynchronizedlyricsframe.h"
+#include "frames/popularimeterframe.h"
+#include "frames/privateframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class FrameFactory::FrameFactoryPrivate
+{
+public:
+  FrameFactoryPrivate() :
+    defaultEncoding(String::Latin1),
+    useDefaultEncoding(false) {}
+
+  String::Type defaultEncoding;
+  bool useDefaultEncoding;
+
+  template <class T> void setTextEncoding(T *frame)
+  {
+    if(useDefaultEncoding)
+      frame->setTextEncoding(defaultEncoding);
+  }
+};
+
+FrameFactory *FrameFactory::factory = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FrameFactory *FrameFactory::instance()
+{
+  if(!factory)
+    factory = new FrameFactory;
+  return factory;
+}
+
+Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) const
+{
+  return createFrame(data, uint(synchSafeInts ? 4 : 3));
+}
+
+Frame *FrameFactory::createFrame(const ByteVector &data, uint version) const
+{
+  Header tagHeader;
+  tagHeader.setMajorVersion(version);
+  return createFrame(data, &tagHeader);
+}
+
+Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) const
+{
+  ByteVector data = origData;
+  uint version = tagHeader->majorVersion();
+  Frame::Header *header = new Frame::Header(data, version);
+  ByteVector frameID = header->frameID();
+
+  // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
+  // characters.  Also make sure that there is data in the frame.
+
+  if(!frameID.size() == (version < 3 ? 3 : 4) ||
+     header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) ||
+     header->frameSize() > data.size())
+  {
+    delete header;
+    return 0;
+  }
+
+  for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
+    if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
+      delete header;
+      return 0;
+    }
+  }
+
+  if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) {
+    // Data lengths are not part of the encoded data, but since they are synch-safe
+    // integers they will be never actually encoded.
+    ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize());
+    frameData = SynchData::decode(frameData);
+    data = data.mid(0, Frame::Header::size(version)) + frameData;
+  }
+
+  // TagLib doesn't mess with encrypted frames, so just treat them
+  // as unknown frames.
+
+#if HAVE_ZLIB == 0
+  if(header->compression()) {
+    debug("Compressed frames are currently not supported.");
+    return new UnknownFrame(data, header);
+  }
+#endif
+  if(header->encryption()) {
+    debug("Encrypted frames are currently not supported.");
+    return new UnknownFrame(data, header);
+  }
+
+  if(!updateFrame(header)) {
+    header->setTagAlterPreservation(true);
+    return new UnknownFrame(data, header);
+  }
+
+  // updateFrame() might have updated the frame ID.
+
+  frameID = header->frameID();
+
+  // This is where things get necissarily nasty.  Here we determine which
+  // Frame subclass (or if none is found simply an Frame) based
+  // on the frame ID.  Since there are a lot of possibilities, that means
+  // a lot of if blocks.
+
+  // Text Identification (frames 4.2)
+
+  if(frameID.startsWith("T")) {
+
+    TextIdentificationFrame *f = frameID != "TXXX"
+      ? new TextIdentificationFrame(data, header)
+      : new UserTextIdentificationFrame(data, header);
+
+    d->setTextEncoding(f);
+
+    if(frameID == "TCON")
+      updateGenre(f);
+
+    return f;
+  }
+
+  // Comments (frames 4.10)
+
+  if(frameID == "COMM") {
+    CommentsFrame *f = new CommentsFrame(data, header);
+    d->setTextEncoding(f);
+    return f;
+  }
+
+  // Attached Picture (frames 4.14)
+
+  if(frameID == "APIC") {
+    AttachedPictureFrame *f = new AttachedPictureFrame(data, header);
+    d->setTextEncoding(f);
+    return f;
+  }
+
+  // ID3v2.2 Attached Picture
+
+       if(frameID == "PIC") {
+    AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header);
+    d->setTextEncoding(f);
+    return f;
+  }
+
+       // Relative Volume Adjustment (frames 4.11)
+
+  if(frameID == "RVA2")
+    return new RelativeVolumeFrame(data, header);
+
+  // Unique File Identifier (frames 4.1)
+
+  if(frameID == "UFID")
+    return new UniqueFileIdentifierFrame(data, header);
+
+  // General Encapsulated Object (frames 4.15)
+
+  if(frameID == "GEOB") {
+    GeneralEncapsulatedObjectFrame *f = new GeneralEncapsulatedObjectFrame(data, header);
+    d->setTextEncoding(f);
+    return f;
+  }
+
+  // URL link (frames 4.3)
+
+  if(frameID.startsWith("W")) {
+    if(frameID != "WXXX") {
+      return new UrlLinkFrame(data, header);
+    }
+    else {
+      UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
+      d->setTextEncoding(f);
+      return f;
+    }
+  }
+
+  // Unsynchronized lyric/text transcription (frames 4.8)
+
+  if(frameID == "USLT") {
+    UnsynchronizedLyricsFrame *f = new UnsynchronizedLyricsFrame(data, header);
+    if(d->useDefaultEncoding)
+      f->setTextEncoding(d->defaultEncoding);
+    return f;
+  }
+
+  // Popularimeter (frames 4.17)
+
+  if(frameID == "POPM")
+    return new PopularimeterFrame(data, header);
+
+  // Private (frames 4.27)
+
+  if(frameID == "PRIV")
+    return new PrivateFrame(data, header);
+
+  return new UnknownFrame(data, header);
+}
+
+String::Type FrameFactory::defaultTextEncoding() const
+{
+  return d->defaultEncoding;
+}
+
+void FrameFactory::setDefaultTextEncoding(String::Type encoding)
+{
+  d->useDefaultEncoding = true;
+  d->defaultEncoding = encoding;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+FrameFactory::FrameFactory()
+{
+  d = new FrameFactoryPrivate;
+}
+
+FrameFactory::~FrameFactory()
+{
+  delete d;
+}
+
+bool FrameFactory::updateFrame(Frame::Header *header) const
+{
+  TagLib::ByteVector frameID = header->frameID();
+
+  switch(header->version()) {
+
+  case 2: // ID3v2.2
+  {
+    if(frameID == "CRM" ||
+       frameID == "EQU" ||
+       frameID == "LNK" ||
+       frameID == "RVA" ||
+       frameID == "TIM" ||
+       frameID == "TSI" ||
+       frameID == "TDA")
+    {
+      debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
+            ".  It will be discarded from the tag.");
+      return false;
+    }
+
+    // ID3v2.2 only used 3 bytes for the frame ID, so we need to convert all of
+    // the frames to their 4 byte ID3v2.4 equivalent.
+
+    convertFrame("BUF", "RBUF", header);
+    convertFrame("CNT", "PCNT", header);
+    convertFrame("COM", "COMM", header);
+    convertFrame("CRA", "AENC", header);
+    convertFrame("ETC", "ETCO", header);
+    convertFrame("GEO", "GEOB", header);
+    convertFrame("IPL", "TIPL", header);
+    convertFrame("MCI", "MCDI", header);
+    convertFrame("MLL", "MLLT", header);
+    convertFrame("POP", "POPM", header);
+    convertFrame("REV", "RVRB", header);
+    convertFrame("SLT", "SYLT", header);
+    convertFrame("STC", "SYTC", header);
+    convertFrame("TAL", "TALB", header);
+    convertFrame("TBP", "TBPM", header);
+    convertFrame("TCM", "TCOM", header);
+    convertFrame("TCO", "TCON", header);
+    convertFrame("TCR", "TCOP", header);
+    convertFrame("TDY", "TDLY", header);
+    convertFrame("TEN", "TENC", header);
+    convertFrame("TFT", "TFLT", header);
+    convertFrame("TKE", "TKEY", header);
+    convertFrame("TLA", "TLAN", header);
+    convertFrame("TLE", "TLEN", header);
+    convertFrame("TMT", "TMED", header);
+    convertFrame("TOA", "TOAL", header);
+    convertFrame("TOF", "TOFN", header);
+    convertFrame("TOL", "TOLY", header);
+    convertFrame("TOR", "TDOR", header);
+    convertFrame("TOT", "TOAL", header);
+    convertFrame("TP1", "TPE1", header);
+    convertFrame("TP2", "TPE2", header);
+    convertFrame("TP3", "TPE3", header);
+    convertFrame("TP4", "TPE4", header);
+    convertFrame("TPA", "TPOS", header);
+    convertFrame("TPB", "TPUB", header);
+    convertFrame("TRC", "TSRC", header);
+    convertFrame("TRD", "TDRC", header);
+    convertFrame("TRK", "TRCK", header);
+    convertFrame("TSS", "TSSE", header);
+    convertFrame("TT1", "TIT1", header);
+    convertFrame("TT2", "TIT2", header);
+    convertFrame("TT3", "TIT3", header);
+    convertFrame("TXT", "TOLY", header);
+    convertFrame("TXX", "TXXX", header);
+    convertFrame("TYE", "TDRC", header);
+    convertFrame("UFI", "UFID", header);
+    convertFrame("ULT", "USLT", header);
+    convertFrame("WAF", "WOAF", header);
+    convertFrame("WAR", "WOAR", header);
+    convertFrame("WAS", "WOAS", header);
+    convertFrame("WCM", "WCOM", header);
+    convertFrame("WCP", "WCOP", header);
+    convertFrame("WPB", "WPUB", header);
+    convertFrame("WXX", "WXXX", header);
+
+    break;
+  }
+
+  case 3: // ID3v2.3
+  {
+    if(frameID == "EQUA" ||
+       frameID == "RVAD" ||
+       frameID == "TIME" ||
+       frameID == "TRDA" ||
+       frameID == "TSIZ" ||
+       frameID == "TDAT")
+    {
+      debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
+            ".  It will be discarded from the tag.");
+      return false;
+    }
+
+    convertFrame("TORY", "TDOR", header);
+    convertFrame("TYER", "TDRC", header);
+
+    break;
+  }
+
+  default:
+
+    // This should catch a typo that existed in TagLib up to and including
+    // version 1.1 where TRDC was used for the year rather than TDRC.
+
+    convertFrame("TRDC", "TDRC", header);
+    break;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FrameFactory::convertFrame(const char *from, const char *to,
+                                Frame::Header *header) const
+{
+  if(header->frameID() != from)
+    return;
+
+  // debug("ID3v2.4 no longer supports the frame type " + String(from) + "  It has" +
+  //       "been converted to the type " + String(to) + ".");
+
+  header->setFrameID(to);
+}
+
+void FrameFactory::updateGenre(TextIdentificationFrame *frame) const
+{
+  StringList fields = frame->fieldList();
+  StringList newfields;
+
+  for(StringList::Iterator it = fields.begin(); it != fields.end(); ++it) {
+    String s = *it;
+    int end = s.find(")");
+
+    if(s.startsWith("(") && end > 0) {
+      // "(12)Genre"
+      String text = s.substr(end + 1);
+      bool ok;
+      int number = s.substr(1, end - 1).toInt(&ok);
+      if(ok && number >= 0 && number <= 255 && !(ID3v1::genre(number) == text))
+        newfields.append(s.substr(1, end - 1));
+      if(!text.isEmpty())
+        newfields.append(text);
+    }
+    else {
+      // "Genre" or "12"
+      newfields.append(s);
+    }
+  }
+
+  if(newfields.isEmpty())
+    fields.append(String::null);
+
+  frame->setText(newfields);
+
+}
diff --git a/src/taglib/mpeg/id3v2/id3v2framefactory.h b/src/taglib/mpeg/id3v2/id3v2framefactory.h
new file mode 100644 (file)
index 0000000..ce573f8
--- /dev/null
@@ -0,0 +1,167 @@
+ /***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V2FRAMEFACTORY_H
+#define TAGLIB_ID3V2FRAMEFACTORY_H
+
+#include "taglib_export.h"
+#include "tbytevector.h"
+#include "id3v2frame.h"
+#include "id3v2header.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    class TextIdentificationFrame;
+
+    //! A factory for creating ID3v2 frames during parsing
+
+    /*!
+     * This factory abstracts away the frame creation process and instantiates
+     * the appropriate ID3v2::Frame subclasses based on the contents of the
+     * data.
+     *
+     * Reimplementing this factory is the key to adding support for frame types
+     * not directly supported by TagLib to your application.  To do so you would
+     * subclass this factory reimplement createFrame().  Then by setting your
+     * factory to be the default factory in ID3v2::Tag constructor or with
+     * MPEG::File::setID3v2FrameFactory() you can implement behavior that will
+     * allow for new ID3v2::Frame subclasses (also provided by you) to be used.
+     *
+     * This implements both <i>abstract factory</i> and <i>singleton</i> patterns
+     * of which more information is available on the web and in software design
+     * textbooks (Notably <i>Design Patters</i>).
+     *
+     * \note You do not need to use this factory to create new frames to add to
+     * an ID3v2::Tag.  You can instantiate frame subclasses directly (with new)
+     * and add them to a tag using ID3v2::Tag::addFrame()
+     *
+     * \see ID3v2::Tag::addFrame()
+     */
+
+    class TAGLIB_EXPORT FrameFactory
+    {
+    public:
+      static FrameFactory *instance();
+      /*!
+       * Create a frame based on \a data.  \a synchSafeInts should only be set
+       * false if we are parsing an old tag (v2.3 or older) that does not support
+       * synchsafe ints.
+       *
+       * \deprecated Please use the method below that accepts a ID3v2::Header
+       * instance in new code.
+       */
+      Frame *createFrame(const ByteVector &data, bool synchSafeInts) const;
+
+      /*!
+       * Create a frame based on \a data.  \a version should indicate the ID3v2
+       * version of the tag.  As ID3v2.4 is the most current version of the
+       * standard 4 is the default.
+       *
+       * \deprecated Please use the method below that accepts a ID3v2::Header
+       * instance in new code.
+       */
+      Frame *createFrame(const ByteVector &data, uint version = 4) const;
+
+      /*!
+       * Create a frame based on \a data.  \a tagHeader should be a valid
+       * ID3v2::Header instance.
+       */
+      // BIC: make virtual
+      Frame *createFrame(const ByteVector &data, Header *tagHeader) const;
+
+      /*!
+       * Returns the default text encoding for text frames.  If setTextEncoding()
+       * has not been explicitly called this will only be used for new text
+       * frames.  However, if this value has been set explicitly all frames will be
+       * converted to this type (unless it's explitly set differently for the
+       * individual frame) when being rendered.
+       *
+       * \see setDefaultTextEncoding()
+       */
+      String::Type defaultTextEncoding() const;
+
+      /*!
+       * Set the default text encoding for all text frames that are created to
+       * \a encoding.  If no value is set the frames with either default to the
+       * encoding type that was parsed and new frames default to Latin1.
+       *
+       * Valid string types for ID3v2 tags are Latin1, UTF8, UTF16 and UTF16BE.
+       *
+       * \see defaultTextEncoding()
+       */
+      void setDefaultTextEncoding(String::Type encoding);
+
+    protected:
+      /*!
+       * Constructs a frame factory.  Because this is a singleton this method is
+       * protected, but may be used for subclasses.
+       */
+      FrameFactory();
+
+      /*!
+       * Destroys the frame factory.  In most cases this will never be called (as
+       * is typical of singletons).
+       */
+      virtual ~FrameFactory();
+
+      /*!
+       * This method checks for compliance to the current ID3v2 standard (2.4)
+       * and does nothing in the common case.  However if a frame is found that
+       * is not compatible with the current standard, this method either updates
+       * the frame or indicates that it should be discarded.
+       *
+       * This method with return true (with or without changes to the frame) if
+       * this frame should be kept or false if it should be discarded.
+       *
+       * See the id3v2.4.0-changes.txt document for further information.
+       */
+      virtual bool updateFrame(Frame::Header *header) const;
+
+    private:
+      FrameFactory(const FrameFactory &);
+      FrameFactory &operator=(const FrameFactory &);
+
+      /*!
+       * This method is used internally to convert a frame from ID \a from to ID
+       * \a to.  If the frame matches the \a from pattern and converts the frame
+       * ID in the \a header or simply does nothing if the frame ID does not match.
+       */
+      void convertFrame(const char *from, const char *to,
+                        Frame::Header *header) const;
+
+      void updateGenre(TextIdentificationFrame *frame) const;
+
+      static FrameFactory *factory;
+
+      class FrameFactoryPrivate;
+      FrameFactoryPrivate *d;
+    };
+
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/id3v2header.cpp b/src/taglib/mpeg/id3v2/id3v2header.cpp
new file mode 100644 (file)
index 0000000..e14adcc
--- /dev/null
@@ -0,0 +1,243 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <iostream>
+#include <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "id3v2header.h"
+#include "id3v2footer.h"
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class Header::HeaderPrivate
+{
+public:
+  HeaderPrivate() : majorVersion(4),
+                    revisionNumber(0),
+                    unsynchronisation(false),
+                    extendedHeader(false),
+                    experimentalIndicator(false),
+                    footerPresent(false),
+                    tagSize(0) {}
+
+  ~HeaderPrivate() {}
+
+  uint majorVersion;
+  uint revisionNumber;
+
+  bool unsynchronisation;
+  bool extendedHeader;
+  bool experimentalIndicator;
+  bool footerPresent;
+
+  uint tagSize;
+
+  static const uint size = 10;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Header::size()
+{
+  return HeaderPrivate::size;
+}
+
+ByteVector Header::fileIdentifier()
+{
+  return ByteVector::fromCString("ID3");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Header::Header()
+{
+  d = new HeaderPrivate;
+}
+
+Header::Header(const ByteVector &data)
+{
+  d = new HeaderPrivate;
+  parse(data);
+}
+
+Header::~Header()
+{
+  delete d;
+}
+
+TagLib::uint Header::majorVersion() const
+{
+  return d->majorVersion;
+}
+
+void Header::setMajorVersion(TagLib::uint version)
+{
+  d->majorVersion = version;
+}
+
+TagLib::uint Header::revisionNumber() const
+{
+  return d->revisionNumber;
+}
+
+bool Header::unsynchronisation() const
+{
+  return d->unsynchronisation;
+}
+
+bool Header::extendedHeader() const
+{
+  return d->extendedHeader;
+}
+
+bool Header::experimentalIndicator() const
+{
+  return d->experimentalIndicator;
+}
+
+bool Header::footerPresent() const
+{
+  return d->footerPresent;
+}
+
+TagLib::uint Header::tagSize() const
+{
+  return d->tagSize;
+}
+
+TagLib::uint Header::completeTagSize() const
+{
+  if(d->footerPresent)
+    return d->tagSize + d->size + Footer::size();
+  else
+    return d->tagSize + d->size;
+}
+
+void Header::setTagSize(uint s)
+{
+  d->tagSize = s;
+}
+
+void Header::setData(const ByteVector &data)
+{
+  parse(data);
+}
+
+ByteVector Header::render() const
+{
+  ByteVector v;
+
+  // add the file identifier -- "ID3"
+  v.append(fileIdentifier());
+
+  // add the version number -- we always render a 2.4.0 tag regardless of what
+  // the tag originally was.
+
+  v.append(char(4));
+  v.append(char(0));
+
+  // Currently we don't actually support writing extended headers, footers or
+  // unsynchronized tags, make sure that the flags are set accordingly.
+
+  d->extendedHeader = false;
+  d->footerPresent = false;
+  d->unsynchronisation = false;
+
+  // render and add the flags
+  std::bitset<8> flags;
+
+  flags[7] = d->unsynchronisation;
+  flags[6] = d->extendedHeader;
+  flags[5] = d->experimentalIndicator;
+  flags[4] = d->footerPresent;
+
+  v.append(char(flags.to_ulong()));
+
+  // add the size
+  v.append(SynchData::fromUInt(d->tagSize));
+
+  return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void Header::parse(const ByteVector &data)
+{
+  if(data.size() < size())
+    return;
+
+
+  // do some sanity checking -- even in ID3v2.3.0 and less the tag size is a
+  // synch-safe integer, so all bytes must be less than 128.  If this is not
+  // true then this is an invalid tag.
+
+  // note that we're doing things a little out of order here -- the size is
+  // later in the bytestream than the version
+
+  ByteVector sizeData = data.mid(6, 4);
+
+  if(sizeData.size() != 4) {
+    d->tagSize = 0;
+    debug("TagLib::ID3v2::Header::parse() - The tag size as read was 0 bytes!");
+    return;
+  }
+
+  for(ByteVector::Iterator it = sizeData.begin(); it != sizeData.end(); it++) {
+    if(uchar(*it) >= 128) {
+      d->tagSize = 0;
+      debug("TagLib::ID3v2::Header::parse() - One of the size bytes in the id3v2 header was greater than the allowed 128.");
+      return;
+    }
+  }
+
+  // The first three bytes, data[0..2], are the File Identifier, "ID3". (structure 3.1 "file identifier")
+
+  // Read the version number from the fourth and fifth bytes.
+  d->majorVersion = data[3];   // (structure 3.1 "major version")
+  d->revisionNumber = data[4]; // (structure 3.1 "revision number")
+
+  // Read the flags, the first four bits of the sixth byte.
+  std::bitset<8> flags(data[5]);
+
+  d->unsynchronisation     = flags[7]; // (structure 3.1.a)
+  d->extendedHeader        = flags[6]; // (structure 3.1.b)
+  d->experimentalIndicator = flags[5]; // (structure 3.1.c)
+  d->footerPresent         = flags[4]; // (structure 3.1.d)
+
+  // Get the size from the remaining four bytes (read above)
+
+  d->tagSize = SynchData::toUInt(sizeData); // (structure 3.1 "size")
+}
diff --git a/src/taglib/mpeg/id3v2/id3v2header.h b/src/taglib/mpeg/id3v2/id3v2header.h
new file mode 100644 (file)
index 0000000..99a7e7f
--- /dev/null
@@ -0,0 +1,175 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V2HEADER_H
+#define TAGLIB_ID3V2HEADER_H
+
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! An implementation of ID3v2 headers
+
+    /*!
+     * This class implements ID3v2 headers.  It attempts to follow, both
+     * semantically and programatically, the structure specified in
+     * the ID3v2 standard.  The API is based on the properties of ID3v2 headers
+     * specified there.  If any of the terms used in this documentation are
+     * unclear please check the specification in the linked section.
+     * (Structure, <a href="id3v2-structure.html#3.1">3.1</a>)
+     */
+
+    class TAGLIB_EXPORT Header
+    {
+    public:
+      /*!
+       * Constructs an empty ID3v2 header.
+       */
+      Header();
+
+      /*!
+       * Constructs an ID3v2 header based on \a data.  parse() is called
+       * immediately.
+       */
+      Header(const ByteVector &data);
+
+      /*!
+       * Destroys the header.
+       */
+      virtual ~Header();
+
+      /*!
+       * Returns the major version number.  (Note: This is the 4, not the 2 in
+       * ID3v2.4.0.  The 2 is implied.)
+       */
+      uint majorVersion() const;
+
+      /*!
+       * Set the the major version number to \a version.  (Note: This is
+       * the 4, not the 2 in ID3v2.4.0.  The 2 is implied.)
+       * \see majorVersion()
+       *
+       * \note This is used by the internal parser; this will not change the
+       * version which is written and in general should not be called by API
+       * users.
+       */
+      void setMajorVersion(uint version);
+
+      /*!
+       * Returns the revision number.  (Note: This is the 0, not the 4 in
+       * ID3v2.4.0.  The 2 is implied.)
+       */
+      uint revisionNumber() const;
+
+      /*!
+       * Returns true if unsynchronisation has been applied to all frames.
+       */
+      bool unsynchronisation() const;
+
+      /*!
+       * Returns true if an extended header is present in the tag.
+       */
+      bool extendedHeader() const;
+
+      /*!
+       * Returns true if the experimental indicator flag is set.
+       */
+      bool experimentalIndicator() const;
+
+      /*!
+       * Returns true if a footer is present in the tag.
+       */
+      bool footerPresent() const;
+      /*!
+       * Returns the tag size in bytes.  This is the size of the frame content.
+       * The size of the \e entire tag will be this plus the header size (10
+       * bytes) and, if present, the footer size (potentially another 10 bytes).
+       *
+       * \note This is the value as read from the header to which TagLib attempts
+       * to provide an API to; it was not a design decision on the part of TagLib
+       * to not include the mentioned portions of the tag in the \e size.
+       *
+       * \see completeTagSize()
+       */
+      uint tagSize() const;
+
+      /*!
+       * Returns the tag size, including the header and, if present, the footer
+       * size.
+       *
+       * \see tagSize()
+       */
+      uint completeTagSize() const;
+
+      /*!
+       * Set the tag size to \a s.
+       * \see tagSize()
+       */
+      void setTagSize(uint s);
+
+      /*!
+       * Returns the size of the header.  Presently this is always 10 bytes.
+       */
+      static uint size();
+
+      /*!
+       * Returns the string used to identify and ID3v2 tag inside of a file.
+       * Presently this is always "ID3".
+       */
+      static ByteVector fileIdentifier();
+
+      /*!
+       * Sets the data that will be used as the header.  10 bytes, starting from
+       * the beginning of \a data are used.
+       */
+      void setData(const ByteVector &data);
+
+      /*!
+       * Renders the Header back to binary format.
+       */
+      ByteVector render() const;
+
+    protected:
+      /*!
+       * Called by setData() to parse the header data.  It makes this information
+       * available through the public API.
+       */
+      void parse(const ByteVector &data);
+
+    private:
+      Header(const Header &);
+      Header &operator=(const Header &);
+
+      class HeaderPrivate;
+      HeaderPrivate *d;
+    };
+
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/id3v2synchdata.cpp b/src/taglib/mpeg/id3v2/id3v2synchdata.cpp
new file mode 100644 (file)
index 0000000..b150948
--- /dev/null
@@ -0,0 +1,77 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <iostream>
+
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+TagLib::uint SynchData::toUInt(const ByteVector &data)
+{
+  uint sum = 0;
+  bool notSynchSafe = false;
+  int last = data.size() > 4 ? 3 : data.size() - 1;
+
+  for(int i = 0; i <= last; i++) {
+    if(data[i] & 0x80) {
+      notSynchSafe = true;
+      break;
+    }
+
+    sum |= (data[i] & 0x7f) << ((last - i) * 7);
+  }
+
+  if(notSynchSafe) {
+    // Invalid data; assume this was created by some buggy software that just
+    // put normal integers here rather than syncsafe ones, and try it that
+    // way.
+    sum = (data.size() > 4) ? data.mid(0, 4).toUInt() : data.toUInt();
+  }
+
+  return sum;
+}
+
+ByteVector SynchData::fromUInt(uint value)
+{
+  ByteVector v(4, 0);
+
+  for(int i = 0; i < 4; i++)
+    v[i] = uchar(value >> ((3 - i) * 7) & 0x7f);
+
+  return v;
+}
+
+ByteVector SynchData::decode(const ByteVector &data)
+{
+  ByteVector result = data;
+
+  ByteVector pattern(2, char(0));
+  pattern[0] = '\xFF';
+  pattern[1] = '\x00';
+
+  return result.replace(pattern, '\xFF');
+}
diff --git a/src/taglib/mpeg/id3v2/id3v2synchdata.h b/src/taglib/mpeg/id3v2/id3v2synchdata.h
new file mode 100644 (file)
index 0000000..86e0bfe
--- /dev/null
@@ -0,0 +1,70 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V2SYNCHDATA_H
+#define TAGLIB_ID3V2SYNCHDATA_H
+
+#include "tbytevector.h"
+#include "taglib.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! A few functions for ID3v2 synch safe integer conversion
+
+    /*!
+     * In the ID3v2.4 standard most integer values are encoded as "synch safe"
+     * integers which are encoded in such a way that they will not give false
+     * MPEG syncs and confuse MPEG decoders.  This namespace provides some
+     * methods for converting to and from these values to ByteVectors for
+     * things rendering and parsing ID3v2 data.
+     */
+
+    namespace SynchData
+    {
+      /*!
+       * This returns the unsigned integer value of \a data where \a data is a
+       * ByteVector that contains a \e synchsafe integer (Structure,
+       * <a href="id3v2-structure.html#6.2">6.2</a>).  The default \a length of
+       * 4 is used if another value is not specified.
+       */
+      TAGLIB_EXPORT uint toUInt(const ByteVector &data);
+
+      /*!
+       * Returns a 4 byte (32 bit) synchsafe integer based on \a value.
+       */
+      TAGLIB_EXPORT ByteVector fromUInt(uint value);
+
+      /*!
+       * Convert the data from unsynchronized data to its original format.
+       */
+      TAGLIB_EXPORT ByteVector decode(const ByteVector &input);
+    }
+
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/id3v2/id3v2tag.cpp b/src/taglib/mpeg/id3v2/id3v2tag.cpp
new file mode 100644 (file)
index 0000000..2ef0027
--- /dev/null
@@ -0,0 +1,475 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tfile.h>
+#include <tdebug.h>
+
+#include "id3v2tag.h"
+#include "id3v2header.h"
+#include "id3v2extendedheader.h"
+#include "id3v2footer.h"
+#include "id3v2synchdata.h"
+
+#include "id3v1genres.h"
+
+#include "frames/textidentificationframe.h"
+#include "frames/commentsframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class ID3v2::Tag::TagPrivate
+{
+public:
+  TagPrivate() : file(0), tagOffset(-1), extendedHeader(0), footer(0), paddingSize(0)
+  {
+    frameList.setAutoDelete(true);
+  }
+  ~TagPrivate()
+  {
+    delete extendedHeader;
+    delete footer;
+  }
+
+  File *file;
+  long tagOffset;
+  const FrameFactory *factory;
+
+  Header header;
+  ExtendedHeader *extendedHeader;
+  Footer *footer;
+
+  int paddingSize;
+
+  FrameListMap frameListMap;
+  FrameList frameList;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ID3v2::Tag::Tag() : TagLib::Tag()
+{
+  d = new TagPrivate;
+  d->factory = FrameFactory::instance();
+}
+
+ID3v2::Tag::Tag(File *file, long tagOffset, const FrameFactory *factory) :
+  TagLib::Tag()
+{
+  d = new TagPrivate;
+
+  d->file = file;
+  d->tagOffset = tagOffset;
+  d->factory = factory;
+
+  read();
+}
+
+ID3v2::Tag::~Tag()
+{
+  delete d;
+}
+
+
+String ID3v2::Tag::title() const
+{
+  if(!d->frameListMap["TIT2"].isEmpty())
+    return d->frameListMap["TIT2"].front()->toString();
+  return String::null;
+}
+
+String ID3v2::Tag::artist() const
+{
+  if(!d->frameListMap["TPE1"].isEmpty())
+    return d->frameListMap["TPE1"].front()->toString();
+  return String::null;
+}
+
+String ID3v2::Tag::album() const
+{
+  if(!d->frameListMap["TALB"].isEmpty())
+    return d->frameListMap["TALB"].front()->toString();
+  return String::null;
+}
+
+String ID3v2::Tag::comment() const
+{
+  const FrameList &comments = d->frameListMap["COMM"];
+
+  if(comments.isEmpty())
+    return String::null;
+
+  for(FrameList::ConstIterator it = comments.begin(); it != comments.end(); ++it)
+  {
+    CommentsFrame *frame = dynamic_cast<CommentsFrame *>(*it);
+
+    if(frame && frame->description().isEmpty())
+      return (*it)->toString();
+  }
+
+  return comments.front()->toString();
+}
+
+String ID3v2::Tag::genre() const
+{
+  // TODO: In the next major version (TagLib 2.0) a list of multiple genres
+  // should be separated by " / " instead of " ".  For the moment to keep
+  // the behavior the same as released versions it is being left with " ".
+
+  if(d->frameListMap["TCON"].isEmpty() ||
+     !dynamic_cast<TextIdentificationFrame *>(d->frameListMap["TCON"].front()))
+  {
+    return String::null;
+  }
+
+  // ID3v2.4 lists genres as the fields in its frames field list.  If the field
+  // is simply a number it can be assumed that it is an ID3v1 genre number.
+  // Here was assume that if an ID3v1 string is present that it should be
+  // appended to the genre string.  Multiple fields will be appended as the
+  // string is built.
+
+  TextIdentificationFrame *f = static_cast<TextIdentificationFrame *>(
+    d->frameListMap["TCON"].front());
+
+  StringList fields = f->fieldList();
+
+  StringList genres;
+
+  for(StringList::Iterator it = fields.begin(); it != fields.end(); ++it) {
+
+    if((*it).isEmpty())
+      continue;
+
+    bool ok;
+    int number = (*it).toInt(&ok);
+    if(ok && number >= 0 && number <= 255) {
+      *it = ID3v1::genre(number);
+    }
+
+    if(std::find(genres.begin(), genres.end(), *it) == genres.end())
+      genres.append(*it);
+  }
+
+  return genres.toString();
+}
+
+TagLib::uint ID3v2::Tag::year() const
+{
+  if(!d->frameListMap["TDRC"].isEmpty())
+    return d->frameListMap["TDRC"].front()->toString().substr(0, 4).toInt();
+  return 0;
+}
+
+TagLib::uint ID3v2::Tag::track() const
+{
+  if(!d->frameListMap["TRCK"].isEmpty())
+    return d->frameListMap["TRCK"].front()->toString().toInt();
+  return 0;
+}
+
+void ID3v2::Tag::setTitle(const String &s)
+{
+  setTextFrame("TIT2", s);
+}
+
+void ID3v2::Tag::setArtist(const String &s)
+{
+  setTextFrame("TPE1", s);
+}
+
+void ID3v2::Tag::setAlbum(const String &s)
+{
+  setTextFrame("TALB", s);
+}
+
+void ID3v2::Tag::setComment(const String &s)
+{
+  if(s.isEmpty()) {
+    removeFrames("COMM");
+    return;
+  }
+
+  if(!d->frameListMap["COMM"].isEmpty())
+    d->frameListMap["COMM"].front()->setText(s);
+  else {
+    CommentsFrame *f = new CommentsFrame(d->factory->defaultTextEncoding());
+    addFrame(f);
+    f->setText(s);
+  }
+}
+
+void ID3v2::Tag::setGenre(const String &s)
+{
+  if(s.isEmpty()) {
+    removeFrames("TCON");
+    return;
+  }
+
+  // iTunes can't handle correctly encoded ID3v2.4 numerical genres.  Just use
+  // strings until iTunes sucks less.
+
+#ifdef NO_ITUNES_HACKS
+
+  int index = ID3v1::genreIndex(s);
+
+  if(index != 255)
+    setTextFrame("TCON", String::number(index));
+  else
+    setTextFrame("TCON", s);
+
+#else
+
+  setTextFrame("TCON", s);
+
+#endif
+}
+
+void ID3v2::Tag::setYear(uint i)
+{
+  if(i <= 0) {
+    removeFrames("TDRC");
+    return;
+  }
+  setTextFrame("TDRC", String::number(i));
+}
+
+void ID3v2::Tag::setTrack(uint i)
+{
+  if(i <= 0) {
+    removeFrames("TRCK");
+    return;
+  }
+  setTextFrame("TRCK", String::number(i));
+}
+
+bool ID3v2::Tag::isEmpty() const
+{
+  return d->frameList.isEmpty();
+}
+
+Header *ID3v2::Tag::header() const
+{
+  return &(d->header);
+}
+
+ExtendedHeader *ID3v2::Tag::extendedHeader() const
+{
+  return d->extendedHeader;
+}
+
+Footer *ID3v2::Tag::footer() const
+{
+  return d->footer;
+}
+
+const FrameListMap &ID3v2::Tag::frameListMap() const
+{
+  return d->frameListMap;
+}
+
+const FrameList &ID3v2::Tag::frameList() const
+{
+  return d->frameList;
+}
+
+const FrameList &ID3v2::Tag::frameList(const ByteVector &frameID) const
+{
+  return d->frameListMap[frameID];
+}
+
+void ID3v2::Tag::addFrame(Frame *frame)
+{
+  d->frameList.append(frame);
+  d->frameListMap[frame->frameID()].append(frame);
+}
+
+void ID3v2::Tag::removeFrame(Frame *frame, bool del)
+{
+  // remove the frame from the frame list
+  FrameList::Iterator it = d->frameList.find(frame);
+  d->frameList.erase(it);
+
+  // ...and from the frame list map
+  it = d->frameListMap[frame->frameID()].find(frame);
+  d->frameListMap[frame->frameID()].erase(it);
+
+  // ...and delete as desired
+  if(del)
+    delete frame;
+}
+
+void ID3v2::Tag::removeFrames(const ByteVector &id)
+{
+    FrameList l = d->frameListMap[id];
+    for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
+      removeFrame(*it, true);
+}
+
+ByteVector ID3v2::Tag::render() const
+{
+  // We need to render the "tag data" first so that we have to correct size to
+  // render in the tag's header.  The "tag data" -- everything that is included
+  // in ID3v2::Header::tagSize() -- includes the extended header, frames and
+  // padding, but does not include the tag's header or footer.
+
+  ByteVector tagData;
+
+  // TODO: Render the extended header.
+
+  // Loop through the frames rendering them and adding them to the tagData.
+
+  for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) {
+    if((*it)->header()->frameID().size() != 4) {
+      debug("A frame of unsupported or unknown type \'"
+          + String((*it)->header()->frameID()) + "\' has been discarded");
+      continue;
+    }
+    if(!(*it)->header()->tagAlterPreservation())
+      tagData.append((*it)->render());
+  }
+
+  // Compute the amount of padding, and append that to tagData.
+
+  uint paddingSize = 0;
+  uint originalSize = d->header.tagSize();
+
+  if(tagData.size() < originalSize)
+    paddingSize = originalSize - tagData.size();
+  else
+    paddingSize = 1024;
+
+  tagData.append(ByteVector(paddingSize, char(0)));
+
+  // Set the tag size.
+  d->header.setTagSize(tagData.size());
+
+  // TODO: This should eventually include d->footer->render().
+  return d->header.render() + tagData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void ID3v2::Tag::read()
+{
+  if(d->file && d->file->isOpen()) {
+
+    d->file->seek(d->tagOffset);
+    d->header.setData(d->file->readBlock(Header::size()));
+
+    // if the tag size is 0, then this is an invalid tag (tags must contain at
+    // least one frame)
+
+    if(d->header.tagSize() == 0)
+      return;
+
+    parse(d->file->readBlock(d->header.tagSize()));
+  }
+}
+
+void ID3v2::Tag::parse(const ByteVector &origData)
+{
+  ByteVector data = origData;
+
+  if(d->header.unsynchronisation() && d->header.majorVersion() <= 3)
+    data = SynchData::decode(data);
+
+  uint frameDataPosition = 0;
+  uint frameDataLength = data.size();
+
+  // check for extended header
+
+  if(d->header.extendedHeader()) {
+    if(!d->extendedHeader)
+      d->extendedHeader = new ExtendedHeader;
+    d->extendedHeader->setData(data);
+    if(d->extendedHeader->size() <= data.size()) {
+      frameDataPosition += d->extendedHeader->size();
+      frameDataLength -= d->extendedHeader->size();
+    }
+  }
+
+  // check for footer -- we don't actually need to parse it, as it *must*
+  // contain the same data as the header, but we do need to account for its
+  // size.
+
+  if(d->header.footerPresent() && Footer::size() <= frameDataLength)
+    frameDataLength -= Footer::size();
+
+  // parse frames
+
+  // Make sure that there is at least enough room in the remaining frame data for
+  // a frame header.
+
+  while(frameDataPosition < frameDataLength - Frame::headerSize(d->header.majorVersion())) {
+
+    // If the next data is position is 0, assume that we've hit the padding
+    // portion of the frame data.
+
+    if(data.at(frameDataPosition) == 0) {
+      if(d->header.footerPresent())
+        debug("Padding *and* a footer found.  This is not allowed by the spec.");
+
+      d->paddingSize = frameDataLength - frameDataPosition;
+      return;
+    }
+
+    Frame *frame = d->factory->createFrame(data.mid(frameDataPosition),
+                                           &d->header);
+
+    if(!frame)
+      return;
+
+    // Checks to make sure that frame parsed correctly.
+
+    if(frame->size() <= 0) {
+      delete frame;
+      return;
+    }
+
+    frameDataPosition += frame->size() + Frame::headerSize(d->header.majorVersion());
+    addFrame(frame);
+  }
+}
+
+void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value)
+{
+  if(value.isEmpty()) {
+    removeFrames(id);
+    return;
+  }
+
+  if(!d->frameListMap[id].isEmpty())
+    d->frameListMap[id].front()->setText(value);
+  else {
+    const String::Type encoding = d->factory->defaultTextEncoding();
+    TextIdentificationFrame *f = new TextIdentificationFrame(id, encoding);
+    addFrame(f);
+    f->setText(value);
+  }
+}
diff --git a/src/taglib/mpeg/id3v2/id3v2tag.h b/src/taglib/mpeg/id3v2/id3v2tag.h
new file mode 100644 (file)
index 0000000..f43f6b7
--- /dev/null
@@ -0,0 +1,300 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ID3V2TAG_H
+#define TAGLIB_ID3V2TAG_H
+
+#include "tag.h"
+#include "tbytevector.h"
+#include "tstring.h"
+#include "tlist.h"
+#include "tmap.h"
+#include "taglib_export.h"
+
+#include "id3v2framefactory.h"
+
+namespace TagLib {
+
+  class File;
+
+  //! An ID3v2 implementation
+
+  /*!
+   * This is a relatively complete and flexible framework for working with ID3v2
+   * tags.
+   *
+   * \see ID3v2::Tag
+   */
+
+  namespace ID3v2 {
+
+    class Header;
+    class ExtendedHeader;
+    class Footer;
+
+    typedef List<Frame *> FrameList;
+    typedef Map<ByteVector, FrameList> FrameListMap;
+
+    //! The main class in the ID3v2 implementation
+
+    /*!
+     * This is the main class in the ID3v2 implementation.  It serves two
+     * functions.  This first, as is obvious from the public API, is to provide a
+     * container for the other ID3v2 related classes.  In addition, through the
+     * read() and parse() protected methods, it provides the most basic level of
+     * parsing.  In these methods the ID3v2 tag is extracted from the file and
+     * split into data components.
+     *
+     * ID3v2 tags have several parts, TagLib attempts to provide an interface
+     * for them all.  header(), footer() and extendedHeader() corespond to those
+     * data structures in the ID3v2 standard and the APIs for the classes that
+     * they return attempt to reflect this.
+     *
+     * Also ID3v2 tags are built up from a list of frames, which are in turn
+     * have a header and a list of fields.  TagLib provides two ways of accessing
+     * the list of frames that are in a given ID3v2 tag.  The first is simply
+     * via the frameList() method.  This is just a list of pointers to the frames.
+     * The second is a map from the frame type -- i.e. "COMM" for comments -- and
+     * a list of frames of that type.  (In some cases ID3v2 allows for multiple
+     * frames of the same type, hence this being a map to a list rather than just
+     * a map to an individual frame.)
+     *
+     * More information on the structure of frames can be found in the ID3v2::Frame
+     * class.
+     *
+     * read() and parse() pass binary data to the other ID3v2 class structures,
+     * they do not handle parsing of flags or fields, for instace.  Those are
+     * handled by similar functions within those classes.
+     *
+     * \note All pointers to data structures within the tag will become invalid
+     * when the tag is destroyed.
+     *
+     * \warning Dealing with the nasty details of ID3v2 is not for the faint of
+     * heart and should not be done without much meditation on the spec.  It's
+     * rather long, but if you're planning on messing with this class and others
+     * that deal with the details of ID3v2 (rather than the nice, safe, abstract
+     * TagLib::Tag and friends), it's worth your time to familiarize yourself
+     * with said spec (which is distrubuted with the TagLib sources).  TagLib
+     * tries to do most of the work, but with a little luck, you can still
+     * convince it to generate invalid ID3v2 tags.  The APIs for ID3v2 assume a
+     * working knowledge of ID3v2 structure.  You're been warned.
+     */
+
+    class TAGLIB_EXPORT Tag : public TagLib::Tag
+    {
+    public:
+      /*!
+       * Constructs an empty ID3v2 tag.
+       *
+       * \note You must create at least one frame for this tag to be valid.
+       */
+      Tag();
+
+      /*!
+       * Constructs an ID3v2 tag read from \a file starting at \a tagOffset.
+       * \a factory specifies which FrameFactory will be used for the
+       * construction of new frames.
+       *
+       * \note You should be able to ignore the \a factory parameter in almost
+       * all situations.  You would want to specify your own FrameFactory
+       * subclass in the case that you are extending TagLib to support additional
+       * frame types, which would be incorperated into your factory.
+       *
+       * \see FrameFactory
+       */
+      Tag(File *file, long tagOffset,
+          const FrameFactory *factory = FrameFactory::instance());
+
+      /*!
+       * Destroys this Tag instance.
+       */
+      virtual ~Tag();
+
+      // Reimplementations.
+
+      virtual String title() const;
+      virtual String artist() const;
+      virtual String album() const;
+      virtual String comment() const;
+      virtual String genre() const;
+      virtual uint year() const;
+      virtual uint track() const;
+
+      virtual void setTitle(const String &s);
+      virtual void setArtist(const String &s);
+      virtual void setAlbum(const String &s);
+      virtual void setComment(const String &s);
+      virtual void setGenre(const String &s);
+      virtual void setYear(uint i);
+      virtual void setTrack(uint i);
+
+      virtual bool isEmpty() const;
+
+      /*!
+       * Returns a pointer to the tag's header.
+       */
+      Header *header() const;
+
+      /*!
+       * Returns a pointer to the tag's extended header or null if there is no
+       * extended header.
+       */
+      ExtendedHeader *extendedHeader() const;
+
+      /*!
+       * Returns a pointer to the tag's footer or null if there is no footer.
+       *
+       * \deprecated I don't see any reason to keep this around since there's
+       * nothing useful to be retrieved from the footer, but well, again, I'm
+       * prone to change my mind, so this gets to stay around until near a
+       * release.
+       */
+      Footer *footer() const;
+
+      /*!
+       * Returns a reference to the frame list map.  This is an FrameListMap of
+       * all of the frames in the tag.
+       *
+       * This is the most convenient structure for accessing the tag's frames.
+       * Many frame types allow multiple instances of the same frame type so this
+       * is a map of lists.  In most cases however there will only be a single
+       * frame of a certain type.
+       *
+       * Let's say for instance that you wanted to access the frame for total
+       * beats per minute -- the TBPM frame.
+       *
+       * \code
+       * TagLib::MPEG::File f("foo.mp3");
+       *
+       * // Check to make sure that it has an ID3v2 tag
+       *
+       * if(f.ID3v2Tag()) {
+       *
+       *   // Get the list of frames for a specific frame type
+       *
+       *   TagLib::ID3v2::FrameList l = f.ID3v2Tag()->frameListMap()["TBPM"];
+       *
+       *   if(!l.isEmpty())
+       *     std::cout << l.front()->toString() << std::endl;
+       * }
+       *
+       * \endcode
+       *
+       * \warning You should not modify this data structure directly, instead
+       * use addFrame() and removeFrame().
+       *
+       * \see frameList()
+       */
+      const FrameListMap &frameListMap() const;
+
+      /*!
+       * Returns a reference to the frame list.  This is an FrameList of all of
+       * the frames in the tag in the order that they were parsed.
+       *
+       * This can be useful if for example you want iterate over the tag's frames
+       * in the order that they occur in the tag.
+       *
+       * \warning You should not modify this data structure directly, instead
+       * use addFrame() and removeFrame().
+       */
+      const FrameList &frameList() const;
+
+      /*!
+       * Returns the frame list for frames with the id \a frameID or an empty
+       * list if there are no frames of that type.  This is just a convenience
+       * and is equivalent to:
+       *
+       * \code
+       * frameListMap()[frameID];
+       * \endcode
+       *
+       * \see frameListMap()
+       */
+      const FrameList &frameList(const ByteVector &frameID) const;
+
+      /*!
+       * Add a frame to the tag.  At this point the tag takes ownership of
+       * the frame and will handle freeing its memory.
+       *
+       * \note Using this method will invalidate any pointers on the list
+       * returned by frameList()
+       */
+      void addFrame(Frame *frame);
+
+      /*!
+       * Remove a frame from the tag.  If \a del is true the frame's memory
+       * will be freed; if it is false, it must be deleted by the user.
+       *
+       * \note Using this method will invalidate any pointers on the list
+       * returned by frameList()
+       */
+      void removeFrame(Frame *frame, bool del = true);
+
+      /*!
+       * Remove all frames of type \a id from the tag and free their memory.
+       *
+       * \note Using this method will invalidate any pointers on the list
+       * returned by frameList()
+       */
+      void removeFrames(const ByteVector &id);
+
+      /*!
+       * Render the tag back to binary data, suitable to be written to disk.
+       */
+      ByteVector render() const;
+
+    protected:
+      /*!
+       * Reads data from the file specified in the constructor.  It does basic
+       * parsing of the data in the largest chunks.  It partitions the tag into
+       * the Header, the body of the tag  (which contains the ExtendedHeader and
+       * frames) and Footer.
+       */
+      void read();
+
+      /*!
+       * This is called by read to parse the body of the tag.  It determines if an
+       * extended header exists and adds frames to the FrameListMap.
+       */
+      void parse(const ByteVector &data);
+
+      /*!
+       * Sets the value of the text frame with the Frame ID \a id to \a value.
+       * If the frame does not exist, it is created.
+       */
+      void setTextFrame(const ByteVector &id, const String &value);
+
+    private:
+      Tag(const Tag &);
+      Tag &operator=(const Tag &);
+
+      class TagPrivate;
+      TagPrivate *d;
+    };
+
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/mpegfile.cpp b/src/taglib/mpeg/mpegfile.cpp
new file mode 100644 (file)
index 0000000..024d811
--- /dev/null
@@ -0,0 +1,593 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tagunion.h>
+#include <id3v2tag.h>
+#include <id3v2header.h>
+#include <id3v1tag.h>
+#include <apefooter.h>
+#include <apetag.h>
+#include <tdebug.h>
+
+#include <bitset>
+
+#include "mpegfile.h"
+#include "mpegheader.h"
+
+using namespace TagLib;
+
+namespace
+{
+  enum { ID3v2Index = 0, APEIndex = 1, ID3v1Index = 2 };
+}
+
+class MPEG::File::FilePrivate
+{
+public:
+  FilePrivate(ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
+    ID3v2FrameFactory(frameFactory),
+    ID3v2Location(-1),
+    ID3v2OriginalSize(0),
+    APELocation(-1),
+    APEFooterLocation(-1),
+    APEOriginalSize(0),
+    ID3v1Location(-1),
+    hasID3v2(false),
+    hasID3v1(false),
+    hasAPE(false),
+    properties(0)
+  {
+
+  }
+
+  ~FilePrivate()
+  {
+    delete properties;
+  }
+
+  const ID3v2::FrameFactory *ID3v2FrameFactory;
+
+  long ID3v2Location;
+  uint ID3v2OriginalSize;
+
+  long APELocation;
+  long APEFooterLocation;
+  uint APEOriginalSize;
+
+  long ID3v1Location;
+
+  TagUnion tag;
+
+  // These indicate whether the file *on disk* has these tags, not if
+  // this data structure does.  This is used in computing offsets.
+
+  bool hasID3v2;
+  bool hasID3v1;
+  bool hasAPE;
+
+  Properties *properties;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG::File::File(FileName file, bool readProperties,
+                 Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+  d = new FilePrivate;
+
+  if(isOpen())
+    read(readProperties, propertiesStyle);
+}
+
+MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
+                 bool readProperties, Properties::ReadStyle propertiesStyle) :
+  TagLib::File(file)
+{
+  d = new FilePrivate(frameFactory);
+
+  if(isOpen())
+    read(readProperties, propertiesStyle);
+}
+
+MPEG::File::~File()
+{
+  delete d;
+}
+
+TagLib::Tag *MPEG::File::tag() const
+{
+  return &d->tag;
+}
+
+MPEG::Properties *MPEG::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool MPEG::File::save()
+{
+  return save(AllTags);
+}
+
+bool MPEG::File::save(int tags)
+{
+  return save(tags, true);
+}
+
+bool MPEG::File::save(int tags, bool stripOthers)
+{
+  if(tags == NoTags && stripOthers)
+    return strip(AllTags);
+
+  if(!ID3v2Tag() && !ID3v1Tag() && !APETag()) {
+
+    if((d->hasID3v1 || d->hasID3v2 || d->hasAPE) && stripOthers)
+      return strip(AllTags);
+
+    return true;
+  }
+
+  if(readOnly()) {
+    debug("MPEG::File::save() -- File is read only.");
+    return false;
+  }
+
+  // Create the tags if we've been asked to.  Copy the values from the tag that
+  // does exist into the new tag.
+
+  if((tags & ID3v2) && ID3v1Tag())
+    Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
+
+  if((tags & ID3v1) && d->tag[ID3v2Index])
+    Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
+
+  bool success = true;
+
+  if(ID3v2 & tags) {
+
+    if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
+
+      if(!d->hasID3v2)
+        d->ID3v2Location = 0;
+
+      insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
+
+      d->hasID3v2 = true;
+
+      // v1 tag location has changed, update if it exists
+
+      if(ID3v1Tag())
+        d->ID3v1Location = findID3v1();
+
+      // APE tag location has changed, update if it exists
+
+      if(APETag())
+       findAPE();
+    }
+    else if(stripOthers)
+      success = strip(ID3v2, false) && success;
+  }
+  else if(d->hasID3v2 && stripOthers)
+    success = strip(ID3v2) && success;
+
+  if(ID3v1 & tags) {
+    if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
+      int offset = d->hasID3v1 ? -128 : 0;
+      seek(offset, End);
+      writeBlock(ID3v1Tag()->render());
+      d->hasID3v1 = true;
+      d->ID3v1Location = findID3v1();
+    }
+    else if(stripOthers)
+      success = strip(ID3v1) && success;
+  }
+  else if(d->hasID3v1 && stripOthers)
+    success = strip(ID3v1, false) && success;
+
+  // Dont save an APE-tag unless one has been created
+
+  if((APE & tags) && APETag()) {
+    if(d->hasAPE)
+      insert(APETag()->render(), d->APELocation, d->APEOriginalSize);
+    else {
+      if(d->hasID3v1) {
+        insert(APETag()->render(), d->ID3v1Location, 0);
+        d->APEOriginalSize = APETag()->footer()->completeTagSize();
+        d->hasAPE = true;
+        d->APELocation = d->ID3v1Location;
+        d->ID3v1Location += d->APEOriginalSize;
+      }
+      else {
+        seek(0, End);
+        d->APELocation = tell();
+       d->APEFooterLocation = d->APELocation
+         + d->tag.access<APE::Tag>(APEIndex, false)->footer()->completeTagSize()
+         - APE::Footer::size();
+        writeBlock(APETag()->render());
+        d->APEOriginalSize = APETag()->footer()->completeTagSize();
+        d->hasAPE = true;
+      }
+    }
+  }
+  else if(d->hasAPE && stripOthers)
+    success = strip(APE, false) && success;
+
+  return success;
+}
+
+ID3v2::Tag *MPEG::File::ID3v2Tag(bool create)
+{
+  return d->tag.access<ID3v2::Tag>(ID3v2Index, create);
+}
+
+ID3v1::Tag *MPEG::File::ID3v1Tag(bool create)
+{
+  return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
+}
+
+APE::Tag *MPEG::File::APETag(bool create)
+{
+  return d->tag.access<APE::Tag>(APEIndex, create);
+}
+
+bool MPEG::File::strip(int tags)
+{
+  return strip(tags, true);
+}
+
+bool MPEG::File::strip(int tags, bool freeMemory)
+{
+  if(readOnly()) {
+    debug("MPEG::File::strip() - Cannot strip tags from a read only file.");
+    return false;
+  }
+
+  if((tags & ID3v2) && d->hasID3v2) {
+    removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
+    d->ID3v2Location = -1;
+    d->ID3v2OriginalSize = 0;
+    d->hasID3v2 = false;
+
+    if(freeMemory)
+      d->tag.set(ID3v2Index, 0);
+
+    // v1 tag location has changed, update if it exists
+
+    if(ID3v1Tag())
+      d->ID3v1Location = findID3v1();
+
+    // APE tag location has changed, update if it exists
+
+   if(APETag())
+      findAPE();
+  }
+
+  if((tags & ID3v1) && d->hasID3v1) {
+    truncate(d->ID3v1Location);
+    d->ID3v1Location = -1;
+    d->hasID3v1 = false;
+
+    if(freeMemory)
+      d->tag.set(ID3v1Index, 0);
+  }
+
+  if((tags & APE) && d->hasAPE) {
+    removeBlock(d->APELocation, d->APEOriginalSize);
+    d->APELocation = -1;
+    d->APEFooterLocation = -1;
+    d->hasAPE = false;
+    if(d->hasID3v1) {
+      if(d->ID3v1Location > d->APELocation)
+        d->ID3v1Location -= d->APEOriginalSize;
+    }
+
+    if(freeMemory)
+      d->tag.set(APEIndex, 0);
+  }
+
+  return true;
+}
+
+void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
+{
+  d->ID3v2FrameFactory = factory;
+}
+
+long MPEG::File::nextFrameOffset(long position)
+{
+  bool foundLastSyncPattern = false;
+
+  ByteVector buffer;
+
+  while(true) {
+    seek(position);
+    buffer = readBlock(bufferSize());
+
+    if(buffer.size() <= 0)
+      return -1;
+
+    if(foundLastSyncPattern && secondSynchByte(buffer[0]))
+      return position - 1;
+
+    for(uint i = 0; i < buffer.size() - 1; i++) {
+      if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
+        return position + i;
+    }
+
+    foundLastSyncPattern = uchar(buffer[buffer.size() - 1]) == 0xff;
+    position += buffer.size();
+  }
+}
+
+long MPEG::File::previousFrameOffset(long position)
+{
+  bool foundFirstSyncPattern = false;
+  ByteVector buffer;
+
+  while (position > 0) {
+    long size = ulong(position) < bufferSize() ? position : bufferSize();
+    position -= size;
+
+    seek(position);
+    buffer = readBlock(size);
+
+    if(buffer.size() <= 0)
+      break;
+
+    if(foundFirstSyncPattern && uchar(buffer[buffer.size() - 1]) == 0xff)
+      return position + buffer.size() - 1;
+
+    for(int i = buffer.size() - 2; i >= 0; i--) {
+      if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
+        return position + i;
+    }
+
+    foundFirstSyncPattern = secondSynchByte(buffer[0]);
+  }
+  return -1;
+}
+
+long MPEG::File::firstFrameOffset()
+{
+  long position = 0;
+
+  if(ID3v2Tag())
+    position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize();
+
+  return nextFrameOffset(position);
+}
+
+long MPEG::File::lastFrameOffset()
+{
+  return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  // Look for an ID3v2 tag
+
+  d->ID3v2Location = findID3v2();
+
+  if(d->ID3v2Location >= 0) {
+
+    d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
+
+    d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
+
+    if(ID3v2Tag()->header()->tagSize() <= 0)
+      d->tag.set(ID3v2Index, 0);
+    else
+      d->hasID3v2 = true;
+  }
+
+  // Look for an ID3v1 tag
+
+  d->ID3v1Location = findID3v1();
+
+  if(d->ID3v1Location >= 0) {
+    d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
+    d->hasID3v1 = true;
+  }
+
+  // Look for an APE tag
+
+  findAPE();
+
+  if(d->APELocation >= 0) {
+
+    d->tag.set(APEIndex, new APE::Tag(this, d->APEFooterLocation));
+    d->APEOriginalSize = APETag()->footer()->completeTagSize();
+    d->hasAPE = true;
+  }
+
+  if(readProperties)
+    d->properties = new Properties(this, propertiesStyle);
+
+  // Make sure that we have our default tag types available.
+
+  ID3v2Tag(true);
+  ID3v1Tag(true);
+}
+
+long MPEG::File::findID3v2()
+{
+  // This method is based on the contents of TagLib::File::find(), but because
+  // of some subtlteies -- specifically the need to look for the bit pattern of
+  // an MPEG sync, it has been modified for use here.
+
+  if(isValid() && ID3v2::Header::fileIdentifier().size() <= bufferSize()) {
+
+    // The position in the file that the current buffer starts at.
+
+    long bufferOffset = 0;
+    ByteVector buffer;
+
+    // These variables are used to keep track of a partial match that happens at
+    // the end of a buffer.
+
+    int previousPartialMatch = -1;
+    bool previousPartialSynchMatch = false;
+
+    // Save the location of the current read pointer.  We will restore the
+    // position using seek() before all returns.
+
+    long originalPosition = tell();
+
+    // Start the search at the beginning of the file.
+
+    seek(0);
+
+    // This loop is the crux of the find method.  There are three cases that we
+    // want to account for:
+    // (1) The previously searched buffer contained a partial match of the search
+    // pattern and we want to see if the next one starts with the remainder of
+    // that pattern.
+    //
+    // (2) The search pattern is wholly contained within the current buffer.
+    //
+    // (3) The current buffer ends with a partial match of the pattern.  We will
+    // note this for use in the next itteration, where we will check for the rest
+    // of the pattern.
+
+    for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {
+
+      // (1) previous partial match
+
+      if(previousPartialSynchMatch && secondSynchByte(buffer[0]))
+        return -1;
+
+      if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) {
+        const int patternOffset = (bufferSize() - previousPartialMatch);
+        if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) {
+          seek(originalPosition);
+          return bufferOffset - bufferSize() + previousPartialMatch;
+        }
+      }
+
+      // (2) pattern contained in current buffer
+
+      long location = buffer.find(ID3v2::Header::fileIdentifier());
+      if(location >= 0) {
+        seek(originalPosition);
+        return bufferOffset + location;
+      }
+
+      int firstSynchByte = buffer.find(char(uchar(255)));
+
+      // Here we have to loop because there could be several of the first
+      // (11111111) byte, and we want to check all such instances until we find
+      // a full match (11111111 111) or hit the end of the buffer.
+
+      while(firstSynchByte >= 0) {
+
+        // if this *is not* at the end of the buffer
+
+        if(firstSynchByte < int(buffer.size()) - 1) {
+          if(secondSynchByte(buffer[firstSynchByte + 1])) {
+            // We've found the frame synch pattern.
+            seek(originalPosition);
+            return -1;
+          }
+          else {
+
+            // We found 11111111 at the end of the current buffer indicating a
+            // partial match of the synch pattern.  The find() below should
+            // return -1 and break out of the loop.
+
+            previousPartialSynchMatch = true;
+          }
+        }
+
+        // Check in the rest of the buffer.
+
+        firstSynchByte = buffer.find(char(uchar(255)), firstSynchByte + 1);
+      }
+
+      // (3) partial match
+
+      previousPartialMatch = buffer.endsWithPartialMatch(ID3v2::Header::fileIdentifier());
+
+      bufferOffset += bufferSize();
+    }
+
+    // Since we hit the end of the file, reset the status before continuing.
+
+    clear();
+
+    seek(originalPosition);
+  }
+
+  return -1;
+}
+
+long MPEG::File::findID3v1()
+{
+  if(isValid()) {
+    seek(-128, End);
+    long p = tell();
+
+    if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+      return p;
+  }
+  return -1;
+}
+
+void MPEG::File::findAPE()
+{
+  if(isValid()) {
+    seek(d->hasID3v1 ? -160 : -32, End);
+
+    long p = tell();
+
+    if(readBlock(8) == APE::Tag::fileIdentifier()) {
+      d->APEFooterLocation = p;
+      seek(d->APEFooterLocation);
+      APE::Footer footer(readBlock(APE::Footer::size()));
+      d->APELocation = d->APEFooterLocation - footer.completeTagSize()
+       + APE::Footer::size();
+      return;
+    }
+  }
+
+  d->APELocation = -1;
+  d->APEFooterLocation = -1;
+}
+
+bool MPEG::File::secondSynchByte(char byte)
+{
+  if(uchar(byte) == 0xff)
+    return false;
+
+  std::bitset<8> b(byte);
+
+  // check to see if the byte matches 111xxxxx
+  return b.test(7) && b.test(6) && b.test(5);
+}
diff --git a/src/taglib/mpeg/mpegfile.h b/src/taglib/mpeg/mpegfile.h
new file mode 100644 (file)
index 0000000..b53c94c
--- /dev/null
@@ -0,0 +1,277 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MPEGFILE_H
+#define TAGLIB_MPEGFILE_H
+
+#include "taglib_export.h"
+#include "tfile.h"
+
+#include "mpegproperties.h"
+
+namespace TagLib {
+
+  namespace ID3v2 { class Tag; class FrameFactory; }
+  namespace ID3v1 { class Tag; }
+  namespace APE { class Tag; }
+
+  //! An implementation of TagLib::File with MPEG (MP3) specific methods
+
+  namespace MPEG {
+
+    //! An MPEG file class with some useful methods specific to MPEG
+
+    /*!
+     * This implements the generic TagLib::File API and additionally provides
+     * access to properties that are distinct to MPEG files, notably access
+     * to the different ID3 tags.
+     */
+
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * This set of flags is used for various operations and is suitable for
+       * being OR-ed together.
+       */
+      enum TagTypes {
+        //! Empty set.  Matches no tag types.
+        NoTags  = 0x0000,
+        //! Matches ID3v1 tags.
+        ID3v1   = 0x0001,
+        //! Matches ID3v2 tags.
+        ID3v2   = 0x0002,
+        //! Matches APE tags.
+        APE     = 0x0004,
+        //! Matches all tag types.
+        AllTags = 0xffff
+      };
+
+      /*!
+       * Contructs an MPEG file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * \deprecated This constructor will be dropped in favor of the one below
+       * in a future version.
+       */
+      File(FileName file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Contructs an MPEG file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.  The frames will be created using
+       * \a frameFactory.
+       */
+      // BIC: merge with the above constructor
+      File(FileName file, ID3v2::FrameFactory *frameFactory,
+           bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns a pointer to a tag that is the union of the ID3v2 and ID3v1
+       * tags. The ID3v2 tag is given priority in reading the information -- if
+       * requested information exists in both the ID3v2 tag and the ID3v1 tag,
+       * the information from the ID3v2 tag will be returned.
+       *
+       * If you would like more granular control over the content of the tags,
+       * with the concession of generality, use the tag-type specific calls.
+       *
+       * \note As this tag is not implemented as an ID3v2 tag or an ID3v1 tag,
+       * but a union of the two this pointer may not be cast to the specific
+       * tag types.
+       *
+       * \see ID3v1Tag()
+       * \see ID3v2Tag()
+       * \see APETag()
+       */
+      virtual Tag *tag() const;
+
+      /*!
+       * Returns the MPEG::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Save the file.  If at least one tag -- ID3v1 or ID3v2 -- exists this
+       * will duplicate its content into the other tag.  This returns true
+       * if saving was successful.
+       *
+       * If neither exists or if both tags are empty, this will strip the tags
+       * from the file.
+       *
+       * This is the same as calling save(AllTags);
+       *
+       * If you would like more granular control over the content of the tags,
+       * with the concession of generality, use paramaterized save call below.
+       *
+       * \see save(int tags)
+       */
+      virtual bool save();
+
+      /*!
+       * Save the file.  This will attempt to save all of the tag types that are
+       * specified by OR-ing together TagTypes values.  The save() method above
+       * uses AllTags.  This returns true if saving was successful.
+       *
+       * This strips all tags not included in the mask, but does not modify them
+       * in memory, so later calls to save() which make use of these tags will
+       * remain valid.  This also strips empty tags.
+       */
+      bool save(int tags);
+
+      /*!
+       * Save the file.  This will attempt to save all of the tag types that are
+       * specified by OR-ing together TagTypes values.  The save() method above
+       * uses AllTags.  This returns true if saving was successful.
+       *
+       * If \a stripOthers is true this strips all tags not included in the mask,
+       * but does not modify them in memory, so later calls to save() which make
+       * use of these tags will remain valid.  This also strips empty tags.
+       */
+      // BIC: combine with the above method
+      bool save(int tags, bool stripOthers);
+
+      /*!
+       * Returns a pointer to the ID3v2 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v2 tag.  If \a create is true it will create
+       * an ID3v2 tag if one does not exist.
+       *
+       * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v2::Tag *ID3v2Tag(bool create = false);
+
+      /*!
+       * Returns a pointer to the ID3v1 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v1 tag.  If \a create is true it will create
+       * an ID3v1 tag if one does not exist.
+       *
+       * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v1::Tag *ID3v1Tag(bool create = false);
+
+      /*!
+       * Returns a pointer to the APE tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid APE tag.  If \a create is true it will create
+       * an APE tag if one does not exist.
+       *
+       * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      APE::Tag *APETag(bool create = false);
+
+      /*!
+       * This will strip the tags that match the OR-ed together TagTypes from the
+       * file.  By default it strips all tags.  It returns true if the tags are
+       * successfully stripped.
+       *
+       * This is equivalent to strip(tags, true)
+       *
+       * \note This will also invalidate pointers to the ID3 and APE tags
+       * as their memory will be freed.
+       */
+      bool strip(int tags = AllTags);
+
+      /*!
+       * This will strip the tags that match the OR-ed together TagTypes from the
+       * file.  By default it strips all tags.  It returns true if the tags are
+       * successfully stripped.
+       *
+       * If \a freeMemory is true the ID3 and APE tags will be deleted and
+       * pointers to them will be invalidated.
+       */
+      // BIC: merge with the method above
+      bool strip(int tags, bool freeMemory);
+
+      /*!
+       * Set the ID3v2::FrameFactory to something other than the default.
+       *
+       * \see ID3v2FrameFactory
+       */
+      void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
+
+      /*!
+       * Returns the position in the file of the first MPEG frame.
+       */
+      long firstFrameOffset();
+
+      /*!
+       * Returns the position in the file of the next MPEG frame,
+       * using the current position as start
+       */
+      long nextFrameOffset(long position);
+
+      /*!
+       * Returns the position in the file of the previous MPEG frame,
+       * using the current position as start
+       */
+      long previousFrameOffset(long position);
+
+      /*!
+       * Returns the position in the file of the last MPEG frame.
+       */
+      long lastFrameOffset();
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+      long findID3v2();
+      long findID3v1();
+      void findAPE();
+
+      /*!
+       * MPEG frames can be recognized by the bit pattern 11111111 111, so the
+       * first byte is easy to check for, however checking to see if the second byte
+       * starts with \e 111 is a bit more tricky, hence this member function.
+       */
+      static bool secondSynchByte(char byte);
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/mpegheader.cpp b/src/taglib/mpeg/mpegheader.cpp
new file mode 100644 (file)
index 0000000..8789530
--- /dev/null
@@ -0,0 +1,276 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <bitset>
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "mpegheader.h"
+
+using namespace TagLib;
+
+class MPEG::Header::HeaderPrivate : public RefCounter
+{
+public:
+  HeaderPrivate() :
+    isValid(false),
+    version(Version1),
+    layer(0),
+    protectionEnabled(false),
+    sampleRate(0),
+    isPadded(false),
+    channelMode(Stereo),
+    isCopyrighted(false),
+    isOriginal(false),
+    frameLength(0),
+    samplesPerFrame(0) {}
+
+  bool isValid;
+  Version version;
+  int layer;
+  bool protectionEnabled;
+  int bitrate;
+  int sampleRate;
+  bool isPadded;
+  ChannelMode channelMode;
+  bool isCopyrighted;
+  bool isOriginal;
+  int frameLength;
+  int samplesPerFrame;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG::Header::Header(const ByteVector &data)
+{
+  d = new HeaderPrivate;
+  parse(data);
+}
+
+MPEG::Header::Header(const Header &h) : d(h.d)
+{
+  d->ref();
+}
+
+MPEG::Header::~Header()
+{
+  if (d->deref())
+    delete d;
+}
+
+bool MPEG::Header::isValid() const
+{
+  return d->isValid;
+}
+
+MPEG::Header::Version MPEG::Header::version() const
+{
+  return d->version;
+}
+
+int MPEG::Header::layer() const
+{
+  return d->layer;
+}
+
+bool MPEG::Header::protectionEnabled() const
+{
+  return d->protectionEnabled;
+}
+
+int MPEG::Header::bitrate() const
+{
+  return d->bitrate;
+}
+
+int MPEG::Header::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+bool MPEG::Header::isPadded() const
+{
+  return d->isPadded;
+}
+
+MPEG::Header::ChannelMode MPEG::Header::channelMode() const
+{
+  return d->channelMode;
+}
+
+bool MPEG::Header::isCopyrighted() const
+{
+  return d->isCopyrighted;
+}
+
+bool MPEG::Header::isOriginal() const
+{
+  return d->isOriginal;
+}
+
+int MPEG::Header::frameLength() const
+{
+  return d->frameLength;
+}
+
+int MPEG::Header::samplesPerFrame() const
+{
+  return d->samplesPerFrame;
+}
+
+MPEG::Header &MPEG::Header::operator=(const Header &h)
+{
+  if(&h == this)
+    return *this;
+
+  if(d->deref())
+    delete d;
+
+  d = h.d;
+  d->ref();
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPEG::Header::parse(const ByteVector &data)
+{
+  if(data.size() < 4 || uchar(data[0]) != 0xff) {
+    debug("MPEG::Header::parse() -- First byte did not match MPEG synch.");
+    return;
+  }
+
+  std::bitset<32> flags(data.toUInt());
+
+  // Check for the second byte's part of the MPEG synch
+
+  if(!flags[23] || !flags[22] || !flags[21]) {
+    debug("MPEG::Header::parse() -- Second byte did not match MPEG synch.");
+    return;
+  }
+
+  // Set the MPEG version
+
+  if(!flags[20] && !flags[19])
+    d->version = Version2_5;
+  else if(flags[20] && !flags[19])
+    d->version = Version2;
+  else if(flags[20] && flags[19])
+    d->version = Version1;
+
+  // Set the MPEG layer
+
+  if(!flags[18] && flags[17])
+    d->layer = 3;
+  else if(flags[18] && !flags[17])
+    d->layer = 2;
+  else if(flags[18] && flags[17])
+    d->layer = 1;
+
+  d->protectionEnabled = !flags[16];
+
+  // Set the bitrate
+
+  static const int bitrates[2][3][16] = {
+    { // Version 1
+      { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1
+      { 0, 32, 48, 56, 64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2
+      { 0, 32, 40, 48, 56,  64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 0 }  // layer 3
+    },
+    { // Version 2 or 2.5
+      { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1
+      { 0, 8,  16, 24, 32, 40, 48, 56,  64,  80,  96,  112, 128, 144, 160, 0 }, // layer 2
+      { 0, 8,  16, 24, 32, 40, 48, 56,  64,  80,  96,  112, 128, 144, 160, 0 }  // layer 3
+    }
+  };
+
+  const int versionIndex = d->version == Version1 ? 0 : 1;
+  const int layerIndex = d->layer > 0 ? d->layer - 1 : 0;
+
+  // The bitrate index is encoded as the first 4 bits of the 3rd byte,
+  // i.e. 1111xxxx
+
+  int i = uchar(data[2]) >> 4;
+
+  d->bitrate = bitrates[versionIndex][layerIndex][i];
+
+  // Set the sample rate
+
+  static const int sampleRates[3][4] = {
+    { 44100, 48000, 32000, 0 }, // Version 1
+    { 22050, 24000, 16000, 0 }, // Version 2
+    { 11025, 12000, 8000,  0 }  // Version 2.5
+  };
+
+  // The sample rate index is encoded as two bits in the 3nd byte, i.e. xxxx11xx
+
+  i = uchar(data[2]) >> 2 & 0x03;
+
+  d->sampleRate = sampleRates[d->version][i];
+
+  if(d->sampleRate == 0) {
+    debug("MPEG::Header::parse() -- Invalid sample rate.");
+    return;
+  }
+
+  // The channel mode is encoded as a 2 bit value at the end of the 3nd byte,
+  // i.e. xxxxxx11
+
+  d->channelMode = ChannelMode((uchar(data[3]) & 0xC0) >> 6);
+
+  // TODO: Add mode extension for completeness
+
+  d->isOriginal = flags[2];
+  d->isCopyrighted = flags[3];
+  d->isPadded = flags[9];
+
+  // Calculate the frame length
+
+  if(d->layer == 1)
+    d->frameLength = 24000 * 2 * d->bitrate / d->sampleRate + int(d->isPadded);
+  else
+    d->frameLength = 72000 * d->bitrate / d->sampleRate + int(d->isPadded);
+
+  // Samples per frame
+
+  static const int samplesPerFrame[3][2] = {
+    // MPEG1, 2/2.5
+    {  384,   384 }, // Layer I
+    { 1152,  1152 }, // Layer II
+    { 1152,   576 }  // Layer III
+  };
+
+  d->samplesPerFrame = samplesPerFrame[layerIndex][versionIndex];
+
+  // Now that we're done parsing, set this to be a valid frame.
+
+  d->isValid = true;
+}
diff --git a/src/taglib/mpeg/mpegheader.h b/src/taglib/mpeg/mpegheader.h
new file mode 100644 (file)
index 0000000..c774de8
--- /dev/null
@@ -0,0 +1,166 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MPEGHEADER_H
+#define TAGLIB_MPEGHEADER_H
+
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  class ByteVector;
+
+  namespace MPEG {
+
+    //! An implementation of MP3 frame headers
+
+    /*!
+     * This is an implementation of MPEG Layer III headers.  The API follows more
+     * or less the binary format of these headers.  I've used
+     * <a href="http://www.mp3-tech.org/programmer/frame_header.html">this</a>
+     * document as a reference.
+     */
+
+    class TAGLIB_EXPORT Header
+    {
+    public:
+      /*!
+       * Parses an MPEG header based on \a data.
+       */
+      Header(const ByteVector &data);
+
+      /*!
+       * Does a shallow copy of \a h.
+       */
+      Header(const Header &h);
+
+      /*!
+       * Destroys this Header instance.
+       */
+      virtual ~Header();
+
+      /*!
+       * Returns true if the frame is at least an appropriate size and has
+       * legal values.
+       */
+      bool isValid() const;
+
+      /*!
+       * The MPEG Version.
+       */
+      enum Version {
+        //! MPEG Version 1
+        Version1 = 0,
+        //! MPEG Version 2
+        Version2 = 1,
+        //! MPEG Version 2.5
+        Version2_5 = 2
+      };
+
+      /*!
+       * Returns the MPEG Version of the header.
+       */
+      Version version() const;
+
+      /*!
+       * Returns the layer version.  This will be between the values 1-3.
+       */
+      int layer() const;
+
+      /*!
+       * Returns true if the MPEG protection bit is enabled.
+       */
+      bool protectionEnabled() const;
+
+      /*!
+       * Returns the bitrate encoded in the header.
+       */
+      int bitrate() const;
+
+      /*!
+       * Returns the sample rate in Hz.
+       */
+      int sampleRate() const;
+
+      /*!
+       * Returns true if the frame is padded.
+       */
+      bool isPadded() const;
+
+      /*!
+       * There are a few combinations or one or two channel audio that are
+       * possible:
+       */
+      enum ChannelMode {
+        //! Stereo
+        Stereo        = 0,
+        //! Stereo
+        JointStereo   = 1,
+        //! Dual Mono
+        DualChannel   = 2,
+        //! Mono
+        SingleChannel = 3
+      };
+
+      /*!
+       * Returns the channel mode for this frame.
+       */
+      ChannelMode channelMode() const;
+
+      /*!
+       * Returns true if the copyrighted bit is set.
+       */
+      bool isCopyrighted() const;
+
+      /*!
+       * Returns true if the "original" bit is set.
+       */
+      bool isOriginal() const;
+
+      /*!
+       * Returns the frame length.
+       */
+      int frameLength() const;
+
+      /*!
+       * Returns the number of frames per sample.
+       */
+      int samplesPerFrame() const;
+
+      /*!
+       * Makes a shallow copy of the header.
+       */
+      Header &operator=(const Header &h);
+
+    private:
+      void parse(const ByteVector &data);
+
+      class HeaderPrivate;
+      HeaderPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/mpegproperties.cpp b/src/taglib/mpeg/mpegproperties.cpp
new file mode 100644 (file)
index 0000000..1c40b96
--- /dev/null
@@ -0,0 +1,254 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tdebug.h>
+#include <tstring.h>
+
+#include "mpegproperties.h"
+#include "mpegfile.h"
+#include "xingheader.h"
+
+using namespace TagLib;
+
+class MPEG::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(File *f, ReadStyle s) :
+    file(f),
+    xingHeader(0),
+    style(s),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0),
+    layer(0),
+    version(Header::Version1),
+    channelMode(Header::Stereo),
+    protectionEnabled(false),
+    isCopyrighted(false),
+    isOriginal(false) {}
+
+  ~PropertiesPrivate()
+  {
+    delete xingHeader;
+  }
+
+  File *file;
+  XingHeader *xingHeader;
+  ReadStyle style;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+  int layer;
+  Header::Version version;
+  Header::ChannelMode channelMode;
+  bool protectionEnabled;
+  bool isCopyrighted;
+  bool isOriginal;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(file, style);
+
+  if(file && file->isOpen())
+    read();
+}
+
+MPEG::Properties::~Properties()
+{
+  delete d;
+}
+
+int MPEG::Properties::length() const
+{
+  return d->length;
+}
+
+int MPEG::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int MPEG::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int MPEG::Properties::channels() const
+{
+  return d->channels;
+}
+
+const MPEG::XingHeader *MPEG::Properties::xingHeader() const
+{
+  return d->xingHeader;
+}
+
+MPEG::Header::Version MPEG::Properties::version() const
+{
+  return d->version;
+}
+
+int MPEG::Properties::layer() const
+{
+  return d->layer;
+}
+
+bool MPEG::Properties::protectionEnabled() const
+{
+  return d->protectionEnabled;
+}
+
+MPEG::Header::ChannelMode MPEG::Properties::channelMode() const
+{
+  return d->channelMode;
+}
+
+bool MPEG::Properties::isCopyrighted() const
+{
+  return d->isCopyrighted;
+}
+
+bool MPEG::Properties::isOriginal() const
+{
+  return d->isOriginal;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPEG::Properties::read()
+{
+  // Since we've likely just looked for the ID3v1 tag, start at the end of the
+  // file where we're least likely to have to have to move the disk head.
+
+  long last = d->file->lastFrameOffset();
+
+  if(last < 0) {
+    debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.");
+    return;
+  }
+
+  d->file->seek(last);
+  Header lastHeader(d->file->readBlock(4));
+
+  long first = d->file->firstFrameOffset();
+
+  if(first < 0) {
+    debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream.");
+    return;
+  }
+
+  if(!lastHeader.isValid()) {
+
+    long pos = last;
+
+    while(pos > first) {
+
+      pos = d->file->previousFrameOffset(pos);
+
+      if(pos < 0)
+        break;
+
+      d->file->seek(pos);
+      Header header(d->file->readBlock(4));
+
+      if(header.isValid()) {
+        lastHeader = header;
+        last = pos;
+        break;
+      }
+    }
+  }
+
+  // Now jump back to the front of the file and read what we need from there.
+
+  d->file->seek(first);
+  Header firstHeader(d->file->readBlock(4));
+
+  if(!firstHeader.isValid() || !lastHeader.isValid()) {
+    debug("MPEG::Properties::read() -- Page headers were invalid.");
+    return;
+  }
+
+  // Check for a Xing header that will help us in gathering information about a
+  // VBR stream.
+
+  int xingHeaderOffset = MPEG::XingHeader::xingHeaderOffset(firstHeader.version(),
+                                                            firstHeader.channelMode());
+
+  d->file->seek(first + xingHeaderOffset);
+  d->xingHeader = new XingHeader(d->file->readBlock(16));
+
+  // Read the length and the bitrate from the Xing header.
+
+  if(d->xingHeader->isValid() &&
+     firstHeader.sampleRate() > 0 &&
+     d->xingHeader->totalFrames() > 0)
+  {
+      double timePerFrame =
+        double(firstHeader.samplesPerFrame()) / firstHeader.sampleRate();
+
+      double length = timePerFrame * d->xingHeader->totalFrames();
+
+      d->length = int(length);
+      d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0;
+  }
+  else {
+    // Since there was no valid Xing header found, we hope that we're in a constant
+    // bitrate file.
+
+    delete d->xingHeader;
+    d->xingHeader = 0;
+
+    // TODO: Make this more robust with audio property detection for VBR without a
+    // Xing header.
+
+    if(firstHeader.frameLength() > 0 && firstHeader.bitrate() > 0) {
+      int frames = (last - first) / firstHeader.frameLength() + 1;
+
+      d->length = int(float(firstHeader.frameLength() * frames) /
+                      float(firstHeader.bitrate() * 125) + 0.5);
+      d->bitrate = firstHeader.bitrate();
+    }
+  }
+
+
+  d->sampleRate = firstHeader.sampleRate();
+  d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2;
+  d->version = firstHeader.version();
+  d->layer = firstHeader.layer();
+  d->protectionEnabled = firstHeader.protectionEnabled();
+  d->channelMode = firstHeader.channelMode();
+  d->isCopyrighted = firstHeader.isCopyrighted();
+  d->isOriginal = firstHeader.isOriginal();
+}
diff --git a/src/taglib/mpeg/mpegproperties.h b/src/taglib/mpeg/mpegproperties.h
new file mode 100644 (file)
index 0000000..17452e1
--- /dev/null
@@ -0,0 +1,118 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MPEGPROPERTIES_H
+#define TAGLIB_MPEGPROPERTIES_H
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+#include "mpegheader.h"
+
+namespace TagLib {
+
+  namespace MPEG {
+
+    class File;
+    class XingHeader;
+
+    //! An implementation of audio property reading for MP3
+
+    /*!
+     * This reads the data from an MPEG Layer III stream found in the
+     * AudioProperties API.
+     */
+
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      /*!
+       * Create an instance of MPEG::Properties with the data read from the
+       * MPEG::File \a file.
+       */
+      Properties(File *file, ReadStyle style = Average);
+
+      /*!
+       * Destroys this MPEG Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+      /*!
+       * Returns a pointer to the XingHeader if one exists or null if no
+       * XingHeader was found.
+       */
+
+      const XingHeader *xingHeader() const;
+
+      /*!
+       * Returns the MPEG Version of the file.
+       */
+      Header::Version version() const;
+
+      /*!
+       * Returns the layer version.  This will be between the values 1-3.
+       */
+      int layer() const;
+
+      /*!
+       * Returns true if the MPEG protection bit is enabled.
+       */
+      bool protectionEnabled() const;
+
+      /*!
+       * Returns the channel mode for this frame.
+       */
+      Header::ChannelMode channelMode() const;
+
+      /*!
+       * Returns true if the copyrighted bit is set.
+       */
+      bool isCopyrighted() const;
+
+      /*!
+       * Returns true if the "original" bit is set.
+       */
+      bool isOriginal() const;
+
+    private:
+      Properties(const Properties &);
+      Properties &operator=(const Properties &);
+
+      void read();
+
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/mpeg/xingheader.cpp b/src/taglib/mpeg/xingheader.cpp
new file mode 100644 (file)
index 0000000..626452e
--- /dev/null
@@ -0,0 +1,115 @@
+/***************************************************************************
+    copyright            : (C) 2003 by Ismael Orenstein
+    email                : orenstein@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "xingheader.h"
+
+using namespace TagLib;
+
+class MPEG::XingHeader::XingHeaderPrivate
+{
+public:
+  XingHeaderPrivate() :
+    frames(0),
+    size(0),
+    valid(false)
+    {}
+
+  uint frames;
+  uint size;
+  bool valid;
+};
+
+MPEG::XingHeader::XingHeader(const ByteVector &data)
+{
+  d = new XingHeaderPrivate;
+  parse(data);
+}
+
+MPEG::XingHeader::~XingHeader()
+{
+  delete d;
+}
+
+bool MPEG::XingHeader::isValid() const
+{
+  return d->valid;
+}
+
+TagLib::uint MPEG::XingHeader::totalFrames() const
+{
+  return d->frames;
+}
+
+TagLib::uint MPEG::XingHeader::totalSize() const
+{
+  return d->size;
+}
+
+int MPEG::XingHeader::xingHeaderOffset(TagLib::MPEG::Header::Version v,
+                                       TagLib::MPEG::Header::ChannelMode c)
+{
+  if(v == MPEG::Header::Version1) {
+    if(c == MPEG::Header::SingleChannel)
+      return 0x15;
+    else
+      return 0x24;
+  }
+  else {
+    if(c == MPEG::Header::SingleChannel)
+      return 0x0D;
+    else
+      return 0x15;
+  }
+}
+
+void MPEG::XingHeader::parse(const ByteVector &data)
+{
+  // Check to see if a valid Xing header is available.
+
+  if(!data.startsWith("Xing") && !data.startsWith("Info"))
+    return;
+
+  // If the XingHeader doesn't contain the number of frames and the total stream
+  // info it's invalid.
+
+  if(!(data[7] & 0x01)) {
+    debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total number of frames.");
+    return;
+  }
+
+  if(!(data[7] & 0x02)) {
+    debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total stream size.");
+    return;
+  }
+
+  d->frames = data.mid(8, 4).toUInt();
+  d->size = data.mid(12, 4).toUInt();
+
+  d->valid = true;
+}
diff --git a/src/taglib/mpeg/xingheader.h b/src/taglib/mpeg/xingheader.h
new file mode 100644 (file)
index 0000000..1ffc03d
--- /dev/null
@@ -0,0 +1,100 @@
+/***************************************************************************
+    copyright            : (C) 2003 by Ismael Orenstein
+    email                : orenstein@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_XINGHEADER_H
+#define TAGLIB_XINGHEADER_H
+
+#include "mpegheader.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  class ByteVector;
+
+  namespace MPEG {
+
+    //! An implementation of the Xing VBR headers
+
+    /*!
+     * This is a minimalistic implementation of the Xing VBR headers.  Xing
+     * headers are often added to VBR (variable bit rate) MP3 streams to make it
+     * easy to compute the length and quality of a VBR stream.  Our implementation
+     * is only concerned with the total size of the stream (so that we can
+     * calculate the total playing time and the average bitrate).  It uses
+     * <a href="http://home.pcisys.net/~melanson/codecs/mp3extensions.txt">this text</a>
+     * and the XMMS sources as references.
+     */
+
+    class TAGLIB_EXPORT XingHeader
+    {
+    public:
+      /*!
+       * Parses a Xing header based on \a data.  The data must be at least 16
+       * bytes long (anything longer than this is discarded).
+       */
+      XingHeader(const ByteVector &data);
+
+      /*!
+       * Destroy this XingHeader instance.
+       */
+      virtual ~XingHeader();
+
+      /*!
+       * Returns true if the data was parsed properly and if there is a valid
+       * Xing header present.
+       */
+      bool isValid() const;
+
+      /*!
+       * Returns the total number of frames.
+       */
+      uint totalFrames() const;
+
+      /*!
+       * Returns the total size of stream in bytes.
+       */
+      uint totalSize() const;
+
+      /*!
+       * Returns the offset for the start of this Xing header, given the
+       * version and channels of the frame
+       */
+      // BIC: rename to offset()
+      static int xingHeaderOffset(TagLib::MPEG::Header::Version v,
+                                  TagLib::MPEG::Header::ChannelMode c);
+
+    private:
+      XingHeader(const XingHeader &);
+      XingHeader &operator=(const XingHeader &);
+
+      void parse(const ByteVector &data);
+
+      class XingHeaderPrivate;
+      XingHeaderPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/ogg/flac/oggflacfile.cpp b/src/taglib/ogg/flac/oggflacfile.cpp
new file mode 100644 (file)
index 0000000..85a5935
--- /dev/null
@@ -0,0 +1,269 @@
+/***************************************************************************
+    copyright            : (C) 2004-2005 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include <xiphcomment.h>
+#include "oggflacfile.h"
+
+using namespace TagLib;
+using TagLib::FLAC::Properties;
+
+class Ogg::FLAC::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    comment(0),
+    properties(0),
+    streamStart(0),
+    streamLength(0),
+    scanned(false),
+    hasXiphComment(false),
+    commentPacket(0) {}
+
+  ~FilePrivate()
+  {
+    delete comment;
+    delete properties;
+  }
+
+  Ogg::XiphComment *comment;
+
+  Properties *properties;
+  ByteVector streamInfoData;
+  ByteVector xiphCommentData;
+  long streamStart;
+  long streamLength;
+  bool scanned;
+
+  bool hasXiphComment;
+  int commentPacket;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::FLAC::File::File(FileName file, bool readProperties,
+                      Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+Ogg::FLAC::File::~File()
+{
+  delete d;
+}
+
+Ogg::XiphComment *Ogg::FLAC::File::tag() const
+{
+  return d->comment;
+}
+
+Properties *Ogg::FLAC::File::audioProperties() const
+{
+  return d->properties;
+}
+
+
+bool Ogg::FLAC::File::save()
+{
+  d->xiphCommentData = d->comment->render(false);
+
+  // Create FLAC metadata-block:
+
+  // Put the size in the first 32 bit (I assume no more than 24 bit are used)
+
+  ByteVector v = ByteVector::fromUInt(d->xiphCommentData.size());
+
+  // Set the type of the metadata-block to be a Xiph / Vorbis comment
+
+  v[0] = 4;
+
+  // Append the comment-data after the 32 bit header
+
+  v.append(d->xiphCommentData);
+
+  // Save the packet at the old spot
+  // FIXME: Use padding if size is increasing
+
+  setPacket(d->commentPacket, v);
+
+  return Ogg::File::save();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Ogg::FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  // Sanity: Check if we really have an Ogg/FLAC file
+
+/*
+  ByteVector oggHeader = packet(0);
+
+  if (oggHeader.mid(28,4) != "fLaC") {
+    debug("Ogg::FLAC::File::read() -- Not an Ogg/FLAC file");
+    setValid(false);
+    return;
+  }*/
+
+  // Look for FLAC metadata, including vorbis comments
+
+  scan();
+
+  if (!d->scanned) {
+    setValid(false);
+    return;
+  }
+
+
+  if(d->hasXiphComment)
+    d->comment = new Ogg::XiphComment(xiphCommentData());
+  else
+    d->comment = new Ogg::XiphComment;
+
+
+  if(readProperties)
+    d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
+}
+
+ByteVector Ogg::FLAC::File::streamInfoData()
+{
+  scan();
+  return d->streamInfoData;
+}
+
+ByteVector Ogg::FLAC::File::xiphCommentData()
+{
+  scan();
+  return d->xiphCommentData;
+}
+
+long Ogg::FLAC::File::streamLength()
+{
+  scan();
+  return d->streamLength;
+}
+
+void Ogg::FLAC::File::scan()
+{
+  // Scan the metadata pages
+
+  if(d->scanned)
+    return;
+
+  if(!isValid())
+    return;
+
+  int ipacket = 0;
+  long overhead = 0;
+
+  ByteVector metadataHeader = packet(ipacket);
+  if(metadataHeader.isNull())
+    return;
+
+  ByteVector header;
+
+  if (!metadataHeader.startsWith("fLaC"))  {
+    // FLAC 1.1.2+
+    if (metadataHeader.mid(1,4) != "FLAC") return;
+
+    if (metadataHeader[5] != 1) return; // not version 1
+
+    metadataHeader = metadataHeader.mid(13);
+  }
+  else {
+    // FLAC 1.1.0 & 1.1.1
+    metadataHeader = packet(++ipacket);
+
+    if(metadataHeader.isNull())
+      return;
+
+  }
+
+  header = metadataHeader.mid(0,4);
+  // Header format (from spec):
+  // <1> Last-metadata-block flag
+  // <7> BLOCK_TYPE
+  //    0 : STREAMINFO
+  //    1 : PADDING
+  //    ..
+  //    4 : VORBIS_COMMENT
+  //    ..
+  // <24> Length of metadata to follow
+
+  char blockType = header[0] & 0x7f;
+  bool lastBlock = (header[0] & 0x80) != 0;
+  uint length = header.mid(1, 3).toUInt();
+  overhead += length;
+
+  // Sanity: First block should be the stream_info metadata
+
+  if(blockType != 0) {
+    debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC stream");
+    return;
+  }
+
+  d->streamInfoData = metadataHeader.mid(4,length);
+
+  // Search through the remaining metadata
+
+  while(!lastBlock) {
+    metadataHeader = packet(++ipacket);
+
+    if(metadataHeader.isNull())
+      return;
+
+    header = metadataHeader.mid(0, 4);
+    blockType = header[0] & 0x7f;
+    lastBlock = (header[0] & 0x80) != 0;
+    length = header.mid(1, 3).toUInt();
+    overhead += length;
+
+    if(blockType == 1) {
+      // debug("Ogg::FLAC::File::scan() -- Padding found");
+    }
+    else if(blockType == 4) {
+      // debug("Ogg::FLAC::File::scan() -- Vorbis-comments found");
+      d->xiphCommentData = metadataHeader.mid(4, length);
+      d->hasXiphComment = true;
+      d->commentPacket = ipacket;
+    }
+    else if(blockType > 5)
+      debug("Ogg::FLAC::File::scan() -- Unknown metadata block");
+
+  }
+
+  // End of metadata, now comes the datastream
+  d->streamStart = overhead;
+  d->streamLength = File::length() - d->streamStart;
+
+  d->scanned = true;
+}
diff --git a/src/taglib/ogg/flac/oggflacfile.h b/src/taglib/ogg/flac/oggflacfile.h
new file mode 100644 (file)
index 0000000..ab1e79d
--- /dev/null
@@ -0,0 +1,118 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_OGGFLACFILE_H
+#define TAGLIB_OGGFLACFILE_H
+
+#include "taglib_export.h"
+#include "oggfile.h"
+#include "xiphcomment.h"
+
+#include "flacproperties.h"
+
+namespace TagLib {
+
+  class Tag;
+
+  namespace Ogg {
+
+  //! An implementation of Ogg FLAC metadata
+
+  /*!
+   * This is implementation of FLAC metadata for Ogg FLAC files.  For "pure"
+   * FLAC files look under the FLAC hiearchy.
+   *
+   * Unlike "pure" FLAC-files, Ogg FLAC only supports Xiph-comments,
+   * while the audio-properties are the same.
+   */
+  namespace FLAC {
+
+    using TagLib::FLAC::Properties;
+
+    //! An implementation of TagLib::File with Ogg/FLAC specific methods
+
+    /*!
+     * This implements and provides an interface for Ogg/FLAC files to the
+     * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+     * the abstract TagLib::File API as well as providing some additional
+     * information specific to Ogg FLAC files.
+     */
+
+    class TAGLIB_EXPORT File : public Ogg::File
+    {
+    public:
+      /*!
+       * Contructs an Ogg/FLAC file from \a file.  If \a readProperties is true
+       * the file's audio properties will also be read using \a propertiesStyle.
+       * If false, \a propertiesStyle is ignored.
+       */
+      File(FileName file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns the Tag for this file.  This will always be a XiphComment.
+       */
+      virtual XiphComment *tag() const;
+
+      /*!
+       * Returns the FLAC::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Save the file.  This will primarily save and update the XiphComment.
+       * Returns true if the save is successful.
+       */
+      virtual bool save();
+
+      /*!
+       * Returns the length of the audio-stream, used by FLAC::Properties for
+       * calculating the bitrate.
+       */
+      long streamLength();
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+      void scan();
+      ByteVector streamInfoData();
+      ByteVector xiphCommentData();
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  } // namespace FLAC
+  } // namespace Ogg
+} // namespace TagLib
+
+#endif
diff --git a/src/taglib/ogg/oggfile.cpp b/src/taglib/ogg/oggfile.cpp
new file mode 100644 (file)
index 0000000..042ed38
--- /dev/null
@@ -0,0 +1,427 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevectorlist.h>
+#include <tmap.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "oggfile.h"
+#include "oggpage.h"
+#include "oggpageheader.h"
+
+using namespace TagLib;
+
+class Ogg::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    streamSerialNumber(0),
+    firstPageHeader(0),
+    lastPageHeader(0),
+    currentPage(0),
+    currentPacketPage(0)
+  {
+    pages.setAutoDelete(true);
+  }
+
+  ~FilePrivate()
+  {
+    delete firstPageHeader;
+    delete lastPageHeader;
+  }
+
+  uint streamSerialNumber;
+  List<Page *> pages;
+  PageHeader *firstPageHeader;
+  PageHeader *lastPageHeader;
+  std::vector< List<int> > packetToPageMap;
+  Map<int, ByteVector> dirtyPackets;
+  List<int> dirtyPages;
+
+  //! The current page for the reader -- used by nextPage()
+  Page *currentPage;
+  //! The current page for the packet parser -- used by packet()
+  Page *currentPacketPage;
+  //! The packets for the currentPacketPage -- used by packet()
+  ByteVectorList currentPackets;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::File::~File()
+{
+  delete d;
+}
+
+ByteVector Ogg::File::packet(uint i)
+{
+  // Check to see if we're called setPacket() for this packet since the last
+  // save:
+
+  if(d->dirtyPackets.contains(i))
+    return d->dirtyPackets[i];
+
+  // If we haven't indexed the page where the packet we're interested in starts,
+  // begin reading pages until we have.
+
+  while(d->packetToPageMap.size() <= i) {
+    if(!nextPage()) {
+      debug("Ogg::File::packet() -- Could not find the requested packet.");
+      return ByteVector::null;
+    }
+  }
+
+  // Start reading at the first page that contains part (or all) of this packet.
+  // If the last read stopped at the packet that we're interested in, don't
+  // reread its packet list.  (This should make sequential packet reads fast.)
+
+  uint pageIndex = d->packetToPageMap[i].front();
+  if(d->currentPacketPage != d->pages[pageIndex]) {
+    d->currentPacketPage = d->pages[pageIndex];
+    d->currentPackets = d->currentPacketPage->packets();
+  }
+
+  // If the packet is completely contained in the first page that it's in, then
+  // just return it now.
+
+  if(d->currentPacketPage->containsPacket(i) & Page::CompletePacket)
+    return d->currentPackets[i - d->currentPacketPage->firstPacketIndex()];
+
+  // If the packet is *not* completely contained in the first page that it's a
+  // part of then that packet trails off the end of the page.  Continue appending
+  // the pages' packet data until we hit a page that either does not end with the
+  // packet that we're fetching or where the last packet is complete.
+
+  ByteVector packet = d->currentPackets.back();
+  while(d->currentPacketPage->containsPacket(i) & Page::EndsWithPacket &&
+        !d->currentPacketPage->header()->lastPacketCompleted())
+  {
+    pageIndex++;
+    if(pageIndex == d->pages.size()) {
+      if(!nextPage()) {
+        debug("Ogg::File::packet() -- Could not find the requested packet.");
+        return ByteVector::null;
+      }
+    }
+    d->currentPacketPage = d->pages[pageIndex];
+    d->currentPackets = d->currentPacketPage->packets();
+    packet.append(d->currentPackets.front());
+  }
+
+  return packet;
+}
+
+void Ogg::File::setPacket(uint i, const ByteVector &p)
+{
+  while(d->packetToPageMap.size() <= i) {
+    if(!nextPage()) {
+      debug("Ogg::File::setPacket() -- Could not set the requested packet.");
+      return;
+    }
+  }
+
+  List<int>::ConstIterator it = d->packetToPageMap[i].begin();
+  for(; it != d->packetToPageMap[i].end(); ++it)
+    d->dirtyPages.sortedInsert(*it, true);
+
+  d->dirtyPackets.insert(i, p);
+}
+
+const Ogg::PageHeader *Ogg::File::firstPageHeader()
+{
+  if(d->firstPageHeader)
+    return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
+
+  long firstPageHeaderOffset = find("OggS");
+
+  if(firstPageHeaderOffset < 0)
+    return 0;
+
+  d->firstPageHeader = new PageHeader(this, firstPageHeaderOffset);
+  return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
+}
+
+const Ogg::PageHeader *Ogg::File::lastPageHeader()
+{
+  if(d->lastPageHeader)
+    return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
+
+  long lastPageHeaderOffset = rfind("OggS");
+
+  if(lastPageHeaderOffset < 0)
+    return 0;
+
+  d->lastPageHeader = new PageHeader(this, lastPageHeaderOffset);
+  return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
+}
+
+bool Ogg::File::save()
+{
+  if(readOnly()) {
+    debug("Ogg::File::save() - Cannot save to a read only file.");
+    return false;
+  }
+
+  List<int> pageGroup;
+
+  for(List<int>::ConstIterator it = d->dirtyPages.begin(); it != d->dirtyPages.end(); ++it) {
+    if(!pageGroup.isEmpty() && pageGroup.back() + 1 != *it) {
+      writePageGroup(pageGroup);
+      pageGroup.clear();
+    }
+    else
+      pageGroup.append(*it);
+  }
+  writePageGroup(pageGroup);
+  d->dirtyPages.clear();
+  d->dirtyPackets.clear();
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::File::File(FileName file) : TagLib::File(file)
+{
+  d = new FilePrivate;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::File::nextPage()
+{
+  long nextPageOffset;
+  int currentPacket;
+
+  if(d->pages.isEmpty()) {
+    currentPacket = 0;
+    nextPageOffset = find("OggS");
+    if(nextPageOffset < 0)
+      return false;
+  }
+  else {
+    if(d->currentPage->header()->lastPageOfStream())
+      return false;
+
+    if(d->currentPage->header()->lastPacketCompleted())
+      currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount();
+    else
+      currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount() - 1;
+
+    nextPageOffset = d->currentPage->fileOffset() + d->currentPage->size();
+  }
+
+  // Read the next page and add it to the page list.
+
+  d->currentPage = new Page(this, nextPageOffset);
+
+  if(!d->currentPage->header()->isValid()) {
+    delete d->currentPage;
+    d->currentPage = 0;
+    return false;
+  }
+
+  d->currentPage->setFirstPacketIndex(currentPacket);
+
+  if(d->pages.isEmpty())
+    d->streamSerialNumber = d->currentPage->header()->streamSerialNumber();
+
+  d->pages.append(d->currentPage);
+
+  // Loop through the packets in the page that we just read appending the
+  // current page number to the packet to page map for each packet.
+
+  for(uint i = 0; i < d->currentPage->packetCount(); i++) {
+    uint currentPacket = d->currentPage->firstPacketIndex() + i;
+    if(d->packetToPageMap.size() <= currentPacket)
+      d->packetToPageMap.push_back(List<int>());
+    d->packetToPageMap[currentPacket].append(d->pages.size() - 1);
+  }
+
+  return true;
+}
+
+void Ogg::File::writePageGroup(const List<int> &thePageGroup)
+{
+  if(thePageGroup.isEmpty())
+    return;
+
+
+  // pages in the pageGroup and packets must be equivalent 
+  // (originalSize and size of packets would not work together), 
+  // therefore we sometimes have to add pages to the group
+  List<int> pageGroup(thePageGroup);
+  while (!d->pages[pageGroup.back()]->header()->lastPacketCompleted()) {
+    if (d->currentPage->header()->pageSequenceNumber() == pageGroup.back()) {
+      if (nextPage() == false) {
+        debug("broken ogg file");
+        return;
+      }
+      pageGroup.append(d->currentPage->header()->pageSequenceNumber());
+    } else {
+      pageGroup.append(pageGroup.back() + 1);
+    }
+  }
+
+  ByteVectorList packets;
+
+  // If the first page of the group isn't dirty, append its partial content here.
+
+  if(!d->dirtyPages.contains(d->pages[pageGroup.front()]->firstPacketIndex()))
+    packets.append(d->pages[pageGroup.front()]->packets().front());
+
+  int previousPacket = -1;
+  int originalSize = 0;
+
+  for(List<int>::ConstIterator it = pageGroup.begin(); it != pageGroup.end(); ++it) {
+    uint firstPacket = d->pages[*it]->firstPacketIndex();
+    uint lastPacket = firstPacket + d->pages[*it]->packetCount() - 1;
+
+    List<int>::ConstIterator last = --pageGroup.end();
+
+    for(uint i = firstPacket; i <= lastPacket; i++) {
+
+      if(it == last && i == lastPacket && !d->dirtyPages.contains(i))
+        packets.append(d->pages[*it]->packets().back());
+      else if(int(i) != previousPacket) {
+        previousPacket = i;
+        packets.append(packet(i));
+      }
+    }
+    originalSize += d->pages[*it]->size();
+  }
+
+  const bool continued = d->pages[pageGroup.front()]->header()->firstPacketContinued();
+  const bool completed = d->pages[pageGroup.back()]->header()->lastPacketCompleted();
+
+  // TODO: This pagination method isn't accurate for what's being done here.
+  // This should account for real possibilities like non-aligned packets and such.
+
+  List<Page *> pages = Page::paginate(packets, Page::SinglePagePerGroup,
+                                      d->streamSerialNumber, pageGroup.front(),
+                                      continued, completed);
+
+  List<Page *> renumberedPages;
+
+  // Correct the page numbering of following pages
+
+  if (pages.back()->header()->pageSequenceNumber() != pageGroup.back()) {
+
+    // TODO: change the internal data structure so that we don't need to hold the 
+    // complete file in memory (is unavoidable at the moment)
+
+    // read the complete stream
+    while(!d->currentPage->header()->lastPageOfStream()) {
+      if(nextPage() == false) {
+        debug("broken ogg file");
+        break;
+      }
+    }
+
+    // create a gap for the new pages
+    int numberOfNewPages = pages.back()->header()->pageSequenceNumber() - pageGroup.back();
+    List<Page *>::Iterator pageIter = d->pages.begin();
+    for(int i = 0; i < pageGroup.back(); i++) {
+      if(pageIter != d->pages.end()) {
+        ++pageIter;
+      }
+      else {
+        debug("Ogg::File::writePageGroup() -- Page sequence is broken in original file.");
+        break;
+      }
+    }
+
+    ++pageIter;
+    for(; pageIter != d->pages.end(); ++pageIter) {
+      Ogg::Page *newPage =
+        (*pageIter)->getCopyWithNewPageSequenceNumber(
+            (*pageIter)->header()->pageSequenceNumber() + numberOfNewPages);
+
+      ByteVector data;
+      data.append(newPage->render());
+      insert(data, newPage->fileOffset(), data.size());
+
+      renumberedPages.append(newPage);
+    }
+  }
+
+  // insert the new data
+
+  ByteVector data;
+  for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it)
+    data.append((*it)->render());
+
+  // The insertion algorithms could also be improve to queue and prioritize data
+  // on the way out.  Currently it requires rewriting the file for every page
+  // group rather than just once; however, for tagging applications there will
+  // generally only be one page group, so it's not worth the time for the
+  // optimization at the moment.
+
+  insert(data, d->pages[pageGroup.front()]->fileOffset(), originalSize);
+
+  // Update the page index to include the pages we just created and to delete the
+  // old pages.
+
+  // First step: Pages that contain the comment data
+
+  for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it) {
+    const unsigned int index = (*it)->header()->pageSequenceNumber();
+    if(index < d->pages.size()) {
+      delete d->pages[index];
+      d->pages[index] = *it;
+    }
+    else if(index == d->pages.size()) {
+      d->pages.append(*it);
+    }
+    else {
+      // oops - there's a hole in the sequence
+      debug("Ogg::File::writePageGroup() -- Page sequence is broken.");
+    }
+  }
+
+  // Second step: the renumbered pages
+
+  for(List<Page *>::ConstIterator it = renumberedPages.begin(); it != renumberedPages.end(); ++it) {
+    const unsigned int index = (*it)->header()->pageSequenceNumber();
+    if(index < d->pages.size()) {
+      delete d->pages[index];
+      d->pages[index] = *it;
+    }
+    else if(index == d->pages.size()) {
+      d->pages.append(*it);
+    }
+    else {
+      // oops - there's a hole in the sequence
+      debug("Ogg::File::writePageGroup() -- Page sequence is broken.");
+    }
+  }
+}
diff --git a/src/taglib/ogg/oggfile.h b/src/taglib/ogg/oggfile.h
new file mode 100644 (file)
index 0000000..e5535f6
--- /dev/null
@@ -0,0 +1,112 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "taglib_export.h"
+#include "tfile.h"
+#include "tbytevectorlist.h"
+
+#ifndef TAGLIB_OGGFILE_H
+#define TAGLIB_OGGFILE_H
+
+namespace TagLib {
+
+  //! A namespace for the classes used by Ogg-based metadata files
+
+  namespace Ogg {
+
+    class PageHeader;
+
+    //! An implementation of TagLib::File with some helpers for Ogg based formats
+
+    /*!
+     * This is an implementation of Ogg file page and packet rendering and is of
+     * use to Ogg based formats.  While the API is small this handles the
+     * non-trivial details of breaking up an Ogg stream into packets and makes
+     * these available (via subclassing) to the codec meta data implementations.
+     */
+
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      virtual ~File();
+
+      /*!
+       * Returns the packet contents for the i-th packet (starting from zero)
+       * in the Ogg bitstream.
+       *
+       * \warning The requires reading at least the packet header for every page
+       * up to the requested page.
+       */
+      ByteVector packet(uint i);
+
+      /*!
+       * Sets the packet with index \a i to the value \a p.
+       */
+      void setPacket(uint i, const ByteVector &p);
+
+      /*!
+       * Returns a pointer to the PageHeader for the first page in the stream or
+       * null if the page could not be found.
+       */
+      const PageHeader *firstPageHeader();
+
+      /*!
+       * Returns a pointer to the PageHeader for the last page in the stream or
+       * null if the page could not be found.
+       */
+      const PageHeader *lastPageHeader();
+
+      virtual bool save();
+
+    protected:
+      /*!
+       * Contructs an Ogg file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * \note This constructor is protected since Ogg::File shouldn't be
+       * instantiated directly but rather should be used through the codec
+       * specific subclasses.
+       */
+      File(FileName file);
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      /*!
+       * Reads the next page and updates the internal "current page" pointer.
+       */
+      bool nextPage();
+      void writePageGroup(const List<int> &group);
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+
+  }
+}
+
+#endif
diff --git a/src/taglib/ogg/oggpage.cpp b/src/taglib/ogg/oggpage.cpp
new file mode 100644 (file)
index 0000000..9b50fef
--- /dev/null
@@ -0,0 +1,340 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "oggpage.h"
+#include "oggpageheader.h"
+#include "oggfile.h"
+
+using namespace TagLib;
+
+class Ogg::Page::PagePrivate
+{
+public:
+  PagePrivate(File *f = 0, long pageOffset = -1) :
+    file(f),
+    fileOffset(pageOffset),
+    packetOffset(0),
+    header(f, pageOffset),
+    firstPacketIndex(-1)
+  {
+    if(file) {
+      packetOffset = fileOffset + header.size();
+      packetSizes = header.packetSizes();
+      dataSize = header.dataSize();
+    }
+  }
+
+  File *file;
+  long fileOffset;
+  long packetOffset;
+  int dataSize;
+  List<int> packetSizes;
+  PageHeader header;
+  int firstPacketIndex;
+  ByteVectorList packets;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::Page::Page(Ogg::File *file, long pageOffset)
+{
+  d = new PagePrivate(file, pageOffset);
+}
+
+Ogg::Page::~Page()
+{
+  delete d;
+}
+
+long Ogg::Page::fileOffset() const
+{
+  return d->fileOffset;
+}
+
+const Ogg::PageHeader *Ogg::Page::header() const
+{
+  return &d->header;
+}
+
+int Ogg::Page::firstPacketIndex() const
+{
+  return d->firstPacketIndex;
+}
+
+void Ogg::Page::setFirstPacketIndex(int index)
+{
+  d->firstPacketIndex = index;
+}
+
+Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
+{
+  int lastPacketIndex = d->firstPacketIndex + packetCount() - 1;
+  if(index < d->firstPacketIndex || index > lastPacketIndex)
+    return DoesNotContainPacket;
+
+  ContainsPacketFlags flags = DoesNotContainPacket;
+
+  if(index == d->firstPacketIndex)
+    flags = ContainsPacketFlags(flags | BeginsWithPacket);
+
+  if(index == lastPacketIndex)
+    flags = ContainsPacketFlags(flags | EndsWithPacket);
+
+  // If there's only one page and it's complete:
+
+  if(packetCount() == 1 &&
+     !d->header.firstPacketContinued() &&
+     d->header.lastPacketCompleted())
+  {
+    flags = ContainsPacketFlags(flags | CompletePacket);
+  }
+
+  // Or if there is more than one page and the page is 
+  // (a) the first page and it's complete or 
+  // (b) the last page and it's complete or 
+  // (c) a page in the middle.
+  else if(packetCount() > 1 &&
+          ((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
+           (flags & EndsWithPacket && d->header.lastPacketCompleted()) ||
+           (!(flags & BeginsWithPacket) && !(flags & EndsWithPacket))))
+  {
+    flags = ContainsPacketFlags(flags | CompletePacket);
+  }
+
+  return flags;
+}
+
+TagLib::uint Ogg::Page::packetCount() const
+{
+  return d->header.packetSizes().size();
+}
+
+ByteVectorList Ogg::Page::packets() const
+{
+  if(!d->packets.isEmpty())
+    return d->packets;
+
+  ByteVectorList l;
+
+  if(d->file && d->header.isValid()) {
+
+    d->file->seek(d->packetOffset);
+
+    List<int> packetSizes = d->header.packetSizes();
+
+    List<int>::ConstIterator it = packetSizes.begin();
+    for(; it != packetSizes.end(); ++it)
+      l.append(d->file->readBlock(*it));
+  }
+  else
+    debug("Ogg::Page::packets() -- attempting to read packets from an invalid page.");
+
+  return l;
+}
+
+int Ogg::Page::size() const
+{
+  return d->header.size() + d->header.dataSize();
+}
+
+ByteVector Ogg::Page::render() const
+{
+  ByteVector data;
+
+  data.append(d->header.render());
+
+  if(d->packets.isEmpty()) {
+    if(d->file) {
+      d->file->seek(d->packetOffset);
+      data.append(d->file->readBlock(d->dataSize));
+    }
+    else
+      debug("Ogg::Page::render() -- this page is empty!");
+  }
+  else {
+    ByteVectorList::ConstIterator it = d->packets.begin();
+    for(; it != d->packets.end(); ++it)
+      data.append(*it);
+  }
+
+  // Compute and set the checksum for the Ogg page.  The checksum is taken over
+  // the entire page with the 4 bytes reserved for the checksum zeroed and then
+  // inserted in bytes 22-25 of the page header.
+
+  ByteVector checksum = ByteVector::fromUInt(data.checksum(), false);
+  for(int i = 0; i < 4; i++)
+    data[i + 22] = checksum[i];
+
+  return data;
+}
+
+List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
+                                      PaginationStrategy strategy,
+                                      uint streamSerialNumber,
+                                      int firstPage,
+                                      bool firstPacketContinued,
+                                      bool lastPacketCompleted,
+                                      bool containsLastPacket)
+{
+  List<Page *> l;
+
+  int totalSize = 0;
+
+  for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
+    totalSize += (*it).size();
+
+  // Handle creation of multiple pages with appropriate pagination.
+  if(strategy == Repaginate || totalSize + packets.size() > 255 * 255) {
+
+    // SPLITSIZE must be a multiple of 255 in order to get the lacing values right
+    // create pages of about 8KB each
+#define SPLITSIZE (32*255)
+
+    int pageIndex = 0;
+
+    for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
+      bool continued = false;
+
+      // mark very first packet?
+      if(firstPacketContinued && it==packets.begin()) {
+        continued = true;
+      }
+
+      // append to buf
+      ByteVector packetBuf;
+      packetBuf.append(*it);
+
+      while(packetBuf.size() > SPLITSIZE) {
+        // output a Page
+        ByteVector packetForOnePage;
+        packetForOnePage.resize(SPLITSIZE);
+        std::copy(packetBuf.begin(), packetBuf.begin() + SPLITSIZE, packetForOnePage.begin());
+
+        ByteVectorList packetList;
+        packetList.append(packetForOnePage);
+        Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued, false, false);
+        l.append(p);
+
+        pageIndex++;
+        continued = true;
+        packetBuf = packetBuf.mid(SPLITSIZE);
+      }
+
+      ByteVectorList::ConstIterator jt = it;
+      ++jt;
+      bool lastPacketInList = (jt == packets.end());
+
+      // output a page for the rest (we output one packet per page, so this one should be completed)
+      ByteVectorList packetList;
+      packetList.append(packetBuf);
+
+      bool isVeryLastPacket = false;
+      if(containsLastPacket) {
+        // mark the very last output page as last of stream
+        ByteVectorList::ConstIterator jt = it;
+        ++jt;
+        if(jt == packets.end()) {
+          isVeryLastPacket = true;
+        }
+      }
+
+      Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued,
+                         lastPacketInList ? lastPacketCompleted : true, 
+                         isVeryLastPacket);
+      pageIndex++;
+
+      l.append(p);
+    }
+  }
+  else {
+    Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
+                       lastPacketCompleted, containsLastPacket);
+    l.append(p);
+  }
+
+  return l;
+}
+
+Ogg::Page* Ogg::Page::getCopyWithNewPageSequenceNumber(int sequenceNumber)
+{
+  Page *pResultPage = NULL;
+
+  // TODO: a copy constructor would be helpful
+
+  if(d->file == 0) {
+    pResultPage = new Page(
+        d->packets,
+        d->header.streamSerialNumber(),
+        sequenceNumber,
+        d->header.firstPacketContinued(),
+        d->header.lastPacketCompleted(),
+        d->header.lastPageOfStream());
+  }
+  else
+  {
+    pResultPage = new Page(d->file, d->fileOffset);
+    pResultPage->d->header.setPageSequenceNumber(sequenceNumber);
+  }
+  return pResultPage;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::Page::Page(const ByteVectorList &packets,
+                uint streamSerialNumber,
+                int pageNumber,
+                bool firstPacketContinued,
+                bool lastPacketCompleted,
+                bool containsLastPacket)
+{
+  d = new PagePrivate;
+
+  ByteVector data;
+  List<int> packetSizes;
+
+  d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued);
+  d->header.setLastPageOfStream(containsLastPacket);
+  d->header.setFirstPacketContinued(firstPacketContinued);
+  d->header.setLastPacketCompleted(lastPacketCompleted);
+  d->header.setStreamSerialNumber(streamSerialNumber);
+  d->header.setPageSequenceNumber(pageNumber);
+
+  // Build a page from the list of packets.
+
+  for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
+    packetSizes.append((*it).size());
+    data.append(*it);
+  }
+  d->packets = packets;
+  d->header.setPacketSizes(packetSizes);
+}
+
diff --git a/src/taglib/ogg/oggpage.h b/src/taglib/ogg/oggpage.h
new file mode 100644 (file)
index 0000000..002d6ba
--- /dev/null
@@ -0,0 +1,211 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_OGGPAGE_H
+#define TAGLIB_OGGPAGE_H
+
+#include "taglib_export.h"
+#include "tbytevectorlist.h"
+
+namespace TagLib {
+
+  namespace Ogg {
+
+    class File;
+    class PageHeader;
+
+    //! An implementation of Ogg pages
+
+    /*!
+     * This is an implementation of the pages that make up an Ogg stream.
+     * This handles parsing pages and breaking them down into packets and handles
+     * the details of packets spanning multiple pages and pages that contiain
+     * multiple packets.
+     *
+     * In most Xiph.org formats the comments are found in the first few packets,
+     * this however is a reasonably complete implementation of Ogg pages that
+     * could potentially be useful for non-meta data purposes.
+     */
+
+    class TAGLIB_EXPORT Page
+    {
+    public:
+      /*!
+       * Read an Ogg page from the \a file at the position \a pageOffset.
+       */
+      Page(File *file, long pageOffset);
+
+      virtual ~Page();
+
+      /*!
+       * Returns the page's position within the file (in bytes).
+       */
+      long fileOffset() const;
+
+      /*!
+       * Returns a pointer to the header for this page.  This pointer will become
+       * invalid when the page is deleted.
+       */
+      const PageHeader *header() const;
+
+      /*! 
+       * Returns a copy of the page with \a sequenceNumber set as sequence number.
+       * 
+       * \see header()
+       * \see PageHeader::setPageSequenceNumber()
+       */
+      Page* getCopyWithNewPageSequenceNumber(int sequenceNumber);
+
+      /*!
+       * Returns the index of the first packet wholly or partially contained in
+       * this page.
+       *
+       * \see setFirstPacketIndex()
+       */
+      int firstPacketIndex() const;
+
+      /*!
+       * Sets the index of the first packet in the page.
+       *
+       * \see firstPacketIndex()
+       */
+      void setFirstPacketIndex(int index);
+
+      /*!
+       * When checking to see if a page contains a given packet this set of flags
+       * represents the possible values for that packets status in the page.
+       *
+       * \see containsPacket()
+       */
+      enum ContainsPacketFlags {
+        //! No part of the packet is contained in the page
+        DoesNotContainPacket = 0x0000,
+        //! The packet is wholly contained in the page
+        CompletePacket       = 0x0001,
+        //! The page starts with the given packet
+        BeginsWithPacket     = 0x0002,
+        //! The page ends with the given packet
+        EndsWithPacket       = 0x0004
+      };
+
+      /*!
+       * Checks to see if the specified \a packet is contained in the current
+       * page.
+       *
+       * \see ContainsPacketFlags
+       */
+      ContainsPacketFlags containsPacket(int index) const;
+
+      /*!
+       * Returns the number of packets (whole or partial) in this page.
+       */
+      uint packetCount() const;
+
+      /*!
+       * Returns a list of the packets in this page.
+       *
+       * \note Either or both the first and last packets may be only partial.
+       * \see PageHeader::firstPacketContinued()
+       */
+      ByteVectorList packets() const;
+
+      /*!
+       * Returns the size of the page in bytes.
+       */
+      int size() const;
+
+      ByteVector render() const;
+
+      /*!
+       * Defines a strategy for pagination, or grouping pages into Ogg packets,
+       * for use with pagination methods.
+       *
+       * \note Yes, I'm aware that this is not a canonical "Strategy Pattern",
+       * the term was simply convenient.
+       */
+      enum PaginationStrategy {
+        /*!
+         * Attempt to put the specified set of packets into a single Ogg packet.
+         * If the sum of the packet data is greater than will fit into a single
+         * Ogg page -- 65280 bytes -- this will fall back to repagination using
+         * the recommended page sizes.
+         */
+        SinglePagePerGroup,
+        /*!
+         * Split the packet or group of packets into pages that conform to the
+         * sizes recommended in the Ogg standard.
+         */
+        Repaginate
+      };
+
+      /*!
+       * Pack \a packets into Ogg pages using the \a strategy for pagination.
+       * The page number indicater inside of the rendered packets will start
+       * with \a firstPage and be incremented for each page rendered.
+       * \a containsLastPacket should be set to true if \a packets contains the
+       * last page in the stream and will set the appropriate flag in the last
+       * rendered Ogg page's header.  \a streamSerialNumber should be set to
+       * the serial number for this stream.
+       *
+       * \note The "absolute granule position" is currently always zeroed using
+       * this method as this suffices for the comment headers.
+       *
+       * \warning The pages returned by this method must be deleted by the user.
+       * You can use List<T>::setAutoDelete(true) to set these pages to be
+       * automatically deleted when this list passes out of scope.
+       *
+       * \see PaginationStrategy
+       * \see List::setAutoDelete()
+       */
+      static List<Page *> paginate(const ByteVectorList &packets,
+                                   PaginationStrategy strategy,
+                                   uint streamSerialNumber,
+                                   int firstPage,
+                                   bool firstPacketContinued = false,
+                                   bool lastPacketCompleted = true,
+                                   bool containsLastPacket = false);
+
+    protected:
+      /*!
+       * Creates an Ogg packet based on the data in \a packets.  The page number
+       * for each page will be set to \a pageNumber.
+       */
+      Page(const ByteVectorList &packets,
+           uint streamSerialNumber,
+           int pageNumber,
+           bool firstPacketContinued = false,
+           bool lastPacketCompleted = true,
+           bool containsLastPacket = false);
+
+    private:
+      Page(const Page &);
+      Page &operator=(const Page &);
+
+      class PagePrivate;
+      PagePrivate *d;
+    };
+  }
+}
+#endif
diff --git a/src/taglib/ogg/oggpageheader.cpp b/src/taglib/ogg/oggpageheader.cpp
new file mode 100644 (file)
index 0000000..5f86fcb
--- /dev/null
@@ -0,0 +1,323 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <stdlib.h>
+
+#include <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <taglib.h>
+
+#include "oggpageheader.h"
+#include "oggfile.h"
+
+using namespace TagLib;
+
+class Ogg::PageHeader::PageHeaderPrivate
+{
+public:
+  PageHeaderPrivate(File *f, long pageOffset) :
+    file(f),
+    fileOffset(pageOffset),
+    isValid(false),
+    firstPacketContinued(false),
+    lastPacketCompleted(false),
+    firstPageOfStream(false),
+    lastPageOfStream(false),
+    absoluteGranularPosition(0),
+    streamSerialNumber(0),
+    pageSequenceNumber(-1),
+    size(0),
+    dataSize(0)
+    {}
+
+  File *file;
+  long fileOffset;
+  bool isValid;
+  List<int> packetSizes;
+  bool firstPacketContinued;
+  bool lastPacketCompleted;
+  bool firstPageOfStream;
+  bool lastPageOfStream;
+  long long absoluteGranularPosition;
+  uint streamSerialNumber;
+  int pageSequenceNumber;
+  int size;
+  int dataSize;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::PageHeader::PageHeader(Ogg::File *file, long pageOffset)
+{
+  d = new PageHeaderPrivate(file, pageOffset);
+  if(file && pageOffset >= 0)
+      read();
+}
+
+Ogg::PageHeader::~PageHeader()
+{
+  delete d;
+}
+
+bool Ogg::PageHeader::isValid() const
+{
+  return d->isValid;
+}
+
+List<int> Ogg::PageHeader::packetSizes() const
+{
+  return d->packetSizes;
+}
+
+void Ogg::PageHeader::setPacketSizes(const List<int> &sizes)
+{
+  d->packetSizes = sizes;
+}
+
+bool Ogg::PageHeader::firstPacketContinued() const
+{
+  return d->firstPacketContinued;
+}
+
+void Ogg::PageHeader::setFirstPacketContinued(bool continued)
+{
+  d->firstPacketContinued = continued;
+}
+
+bool Ogg::PageHeader::lastPacketCompleted() const
+{
+  return d->lastPacketCompleted;
+}
+
+void Ogg::PageHeader::setLastPacketCompleted(bool completed)
+{
+  d->lastPacketCompleted = completed;
+}
+
+bool Ogg::PageHeader::firstPageOfStream() const
+{
+  return d->firstPageOfStream;
+}
+
+void Ogg::PageHeader::setFirstPageOfStream(bool first)
+{
+  d->firstPageOfStream = first;
+}
+
+bool Ogg::PageHeader::lastPageOfStream() const
+{
+  return d->lastPageOfStream;
+}
+
+void Ogg::PageHeader::setLastPageOfStream(bool last)
+{
+  d->lastPageOfStream = last;
+}
+
+long long Ogg::PageHeader::absoluteGranularPosition() const
+{
+  return d->absoluteGranularPosition;
+}
+
+void Ogg::PageHeader::setAbsoluteGranularPosition(long long agp)
+{
+  d->absoluteGranularPosition = agp;
+}
+
+int Ogg::PageHeader::pageSequenceNumber() const
+{
+  return d->pageSequenceNumber;
+}
+
+void Ogg::PageHeader::setPageSequenceNumber(int sequenceNumber)
+{
+  d->pageSequenceNumber = sequenceNumber;
+}
+
+TagLib::uint Ogg::PageHeader::streamSerialNumber() const
+{
+  return d->streamSerialNumber;
+}
+
+void Ogg::PageHeader::setStreamSerialNumber(uint n)
+{
+  d->streamSerialNumber = n;
+}
+
+int Ogg::PageHeader::size() const
+{
+  return d->size;
+}
+
+int Ogg::PageHeader::dataSize() const
+{
+  return d->dataSize;
+}
+
+ByteVector Ogg::PageHeader::render() const
+{
+  ByteVector data;
+
+  // capture patern
+
+  data.append("OggS");
+
+  // stream structure version
+
+  data.append(char(0));
+
+  // header type flag
+
+  std::bitset<8> flags;
+  flags[0] = d->firstPacketContinued;
+  flags[1] = d->pageSequenceNumber == 0;
+  flags[2] = d->lastPageOfStream;
+
+  data.append(char(flags.to_ulong()));
+
+  // absolute granular position
+
+  data.append(ByteVector::fromLongLong(d->absoluteGranularPosition, false));
+
+  // stream serial number
+
+  data.append(ByteVector::fromUInt(d->streamSerialNumber, false));
+
+  // page sequence number
+
+  data.append(ByteVector::fromUInt(d->pageSequenceNumber, false));
+
+  // checksum -- this is left empty and should be filled in by the Ogg::Page
+  // class
+
+  data.append(ByteVector(4, 0));
+
+  // page segment count and page segment table
+
+  ByteVector pageSegments = lacingValues();
+
+  data.append(char(uchar(pageSegments.size())));
+  data.append(pageSegments);
+
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Ogg::PageHeader::read()
+{
+  d->file->seek(d->fileOffset);
+
+  // An Ogg page header is at least 27 bytes, so we'll go ahead and read that
+  // much and then get the rest when we're ready for it.
+
+  ByteVector data = d->file->readBlock(27);
+
+  // Sanity check -- make sure that we were in fact able to read as much data as
+  // we asked for and that the page begins with "OggS".
+
+  if(data.size() != 27 || !data.startsWith("OggS")) {
+    debug("Ogg::PageHeader::read() -- error reading page header");
+    return;
+  }
+
+  std::bitset<8> flags(data[5]);
+
+  d->firstPacketContinued = flags.test(0);
+  d->firstPageOfStream = flags.test(1);
+  d->lastPageOfStream = flags.test(2);
+
+  d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false);
+  d->streamSerialNumber = data.mid(14, 4).toUInt(false);
+  d->pageSequenceNumber = data.mid(18, 4).toUInt(false);
+
+  // Byte number 27 is the number of page segments, which is the only variable
+  // length portion of the page header.  After reading the number of page
+  // segments we'll then read in the corresponding data for this count.
+
+  int pageSegmentCount = uchar(data[26]);
+
+  ByteVector pageSegments = d->file->readBlock(pageSegmentCount);
+
+  // Another sanity check.
+
+  if(pageSegmentCount < 1 || int(pageSegments.size()) != pageSegmentCount)
+    return;
+
+  // The base size of an Ogg page 27 bytes plus the number of lacing values.
+
+  d->size = 27 + pageSegmentCount;
+
+  int packetSize = 0;
+
+  for(int i = 0; i < pageSegmentCount; i++) {
+    d->dataSize += uchar(pageSegments[i]);
+    packetSize += uchar(pageSegments[i]);
+
+    if(uchar(pageSegments[i]) < 255) {
+      d->packetSizes.append(packetSize);
+      packetSize = 0;
+    }
+  }
+
+  if(packetSize > 0) {
+    d->packetSizes.append(packetSize);
+    d->lastPacketCompleted = false;
+  }
+  else
+    d->lastPacketCompleted = true;
+
+  d->isValid = true;
+}
+
+ByteVector Ogg::PageHeader::lacingValues() const
+{
+  ByteVector data;
+
+  List<int> sizes = d->packetSizes;
+  for(List<int>::ConstIterator it = sizes.begin(); it != sizes.end(); ++it) {
+
+    // The size of a packet in an Ogg page is indicated by a series of "lacing
+    // values" where the sum of the values is the packet size in bytes.  Each of
+    // these values is a byte.  A value of less than 255 (0xff) indicates the end
+    // of the packet.
+
+    div_t n = div(*it, 255);
+
+    for(int i = 0; i < n.quot; i++)
+      data.append(char(uchar(255)));
+
+    if(it != --sizes.end() || d->lastPacketCompleted)
+      data.append(char(uchar(n.rem)));
+  }
+
+  return data;
+}
diff --git a/src/taglib/ogg/oggpageheader.h b/src/taglib/ogg/oggpageheader.h
new file mode 100644 (file)
index 0000000..4c683fe
--- /dev/null
@@ -0,0 +1,232 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_OGGPAGEHEADER_H
+#define TAGLIB_OGGPAGEHEADER_H
+
+#include "tlist.h"
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace Ogg {
+
+    class File;
+
+    //! An implementation of the page headers associated with each Ogg::Page
+
+    /*!
+     * This class implements Ogg page headers which contain the information
+     * about Ogg pages needed to break them into packets which can be passed on
+     * to the codecs.
+     */
+
+    class TAGLIB_EXPORT PageHeader
+    {
+    public:
+      /*!
+       * Reads a PageHeader from \a file starting at \a pageOffset.  The defaults
+       * create a page with no (and as such, invalid) data that must be set
+       * later.
+       */
+      PageHeader(File *file = 0, long pageOffset = -1);
+
+      /*!
+       * Deletes this instance of the PageHeader.
+       */
+      virtual ~PageHeader();
+
+      /*!
+       * Returns true if the header parsed properly and is valid.
+       */
+      bool isValid() const;
+
+      /*!
+       * Ogg pages contain a list of packets (which are used by the contained
+       * codecs).  The sizes of these pages is encoded in the page header.  This
+       * returns a list of the packet sizes in bytes.
+       *
+       * \see setPacketSizes()
+       */
+      List<int> packetSizes() const;
+
+      /*!
+       * Sets the sizes of the packets in this page to \a sizes.  Internally this
+       * updates the lacing values in the header.
+       *
+       * \see packetSizes()
+       */
+      void setPacketSizes(const List<int> &sizes);
+
+      /*!
+       * Some packets can be <i>continued</i> across multiple pages.  If the
+       * first packet in the current page is a continuation this will return
+       * true.  If this is page starts with a new packet this will return false.
+       *
+       * \see lastPacketCompleted()
+       * \see setFirstPacketContinued()
+       */
+      bool firstPacketContinued() const;
+
+      /*!
+       * Sets the internal flag indicating if the first packet in this page is
+       * continued to \a continued.
+       *
+       * \see firstPacketContinued()
+       */
+      void setFirstPacketContinued(bool continued);
+
+      /*!
+       * Returns true if the last packet of this page is completely contained in
+       * this page.
+       *
+       * \see firstPacketContinued()
+       * \see setLastPacketCompleted()
+       */
+      bool lastPacketCompleted() const;
+
+      /*!
+       * Sets the internal flag indicating if the last packet in this page is
+       * complete to \a completed.
+       *
+       * \see lastPacketCompleted()
+       */
+      void setLastPacketCompleted(bool completed);
+
+      /*!
+       * This returns true if this is the first page of the Ogg (logical) stream.
+       *
+       * \see setFirstPageOfStream()
+       */
+      bool firstPageOfStream() const;
+
+      /*!
+       * Marks this page as the first page of the Ogg stream.
+       *
+       * \see firstPageOfStream()
+       */
+      void setFirstPageOfStream(bool first);
+
+      /*!
+       * This returns true if this is the last page of the Ogg (logical) stream.
+       *
+       * \see setLastPageOfStream()
+       */
+      bool lastPageOfStream() const;
+
+      /*!
+       * Marks this page as the last page of the Ogg stream.
+       *
+       * \see lastPageOfStream()
+       */
+      void setLastPageOfStream(bool last);
+
+      /*!
+       * A special value of containing the position of the packet to be
+       * interpreted by the codec.  In the case of Vorbis this contains the PCM
+       * value and is used to calculate the length of the stream.
+       *
+       * \see setAbsoluteGranularPosition()
+       */
+      long long absoluteGranularPosition() const;
+
+      /*!
+       * A special value of containing the position of the packet to be
+       * interpreted by the codec.  It is only supported here so that it may be
+       * coppied from one page to another.
+       *
+       * \see absoluteGranularPosition()
+       */
+      void setAbsoluteGranularPosition(long long agp);
+
+      /*!
+       * Every Ogg logical stream is given a random serial number which is common
+       * to every page in that logical stream.  This returns the serial number of
+       * the stream associated with this packet.
+       *
+       * \see setStreamSerialNumber()
+       */
+      uint streamSerialNumber() const;
+
+      /*!
+       * Every Ogg logical stream is given a random serial number which is common
+       * to every page in that logical stream.  This sets this pages serial
+       * number.  This method should be used when adding new pages to a logical
+       * stream.
+       *
+       * \see streamSerialNumber()
+       */
+      void setStreamSerialNumber(uint n);
+
+      /*!
+       * Returns the index of the page within the Ogg stream.  This helps make it
+       * possible to determine if pages have been lost.
+       *
+       * \see setPageSequenceNumber()
+       */
+      int pageSequenceNumber() const;
+
+      /*!
+       * Sets the page's position in the stream to \a sequenceNumber.
+       *
+       * \see pageSequenceNumber()
+       */
+      void setPageSequenceNumber(int sequenceNumber);
+
+      /*!
+       * Returns the complete header size.
+       */
+      int size() const;
+
+      /*!
+       * Returns the size of the data portion of the page -- i.e. the size of the
+       * page less the header size.
+       */
+      int dataSize() const;
+
+      /*!
+       * Render the page header to binary data.
+       *
+       * \note The checksum -- bytes 22 - 25 -- will be left empty and must be
+       * filled in when rendering the entire page.
+       */
+      ByteVector render() const;
+
+    private:
+      PageHeader(const PageHeader &);
+      PageHeader &operator=(const PageHeader &);
+
+      void read();
+      ByteVector lacingValues() const;
+
+      class PageHeaderPrivate;
+      PageHeaderPrivate *d;
+    };
+
+  }
+}
+
+#endif
diff --git a/src/taglib/ogg/speex/speexfile.cpp b/src/taglib/ogg/speex/speexfile.cpp
new file mode 100644 (file)
index 0000000..a1391b8
--- /dev/null
@@ -0,0 +1,112 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+                           (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "speexfile.h"
+
+using namespace TagLib;
+using namespace TagLib::Ogg;
+
+class Speex::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    comment(0),
+    properties(0) {}
+
+  ~FilePrivate()
+  {
+    delete comment;
+    delete properties;
+  }
+
+  Ogg::XiphComment *comment;
+  Properties *properties;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Speex::File::File(FileName file, bool readProperties,
+                   Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+Speex::File::~File()
+{
+  delete d;
+}
+
+Ogg::XiphComment *Speex::File::tag() const
+{
+  return d->comment;
+}
+
+Speex::Properties *Speex::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool Speex::File::save()
+{
+  if(!d->comment)
+    d->comment = new Ogg::XiphComment;
+
+  setPacket(1, d->comment->render());
+
+  return Ogg::File::save();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Speex::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  ByteVector speexHeaderData = packet(0);
+
+  if(!speexHeaderData.startsWith("Speex   ")) {
+    debug("Speex::File::read() -- invalid Speex identification header");
+    return;
+  }
+
+  ByteVector commentHeaderData = packet(1);
+
+  d->comment = new Ogg::XiphComment(commentHeaderData);
+
+  if(readProperties)
+    d->properties = new Properties(this, propertiesStyle);
+}
diff --git a/src/taglib/ogg/speex/speexfile.h b/src/taglib/ogg/speex/speexfile.h
new file mode 100644 (file)
index 0000000..b744c25
--- /dev/null
@@ -0,0 +1,99 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+                           (original Vorbis implementation)
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_SPEEXFILE_H
+#define TAGLIB_SPEEXFILE_H
+
+#include "oggfile.h"
+#include "xiphcomment.h"
+
+#include "speexproperties.h"
+
+namespace TagLib {
+
+  namespace Ogg {
+
+    //! A namespace containing classes for Speex metadata
+
+    namespace Speex {
+
+      //! An implementation of Ogg::File with Speex specific methods
+
+      /*!
+       * This is the central class in the Ogg Speex metadata processing collection
+       * of classes.  It's built upon Ogg::File which handles processing of the Ogg
+       * logical bitstream and breaking it down into pages which are handled by
+       * the codec implementations, in this case Speex specifically.
+       */
+
+      class TAGLIB_EXPORT File : public Ogg::File
+      {
+      public:
+        /*!
+         * Contructs a Speex file from \a file.  If \a readProperties is true the
+         * file's audio properties will also be read using \a propertiesStyle.  If
+         * false, \a propertiesStyle is ignored.
+         */
+        File(FileName file, bool readProperties = true,
+             Properties::ReadStyle propertiesStyle = Properties::Average);
+
+        /*!
+         * Destroys this instance of the File.
+         */
+        virtual ~File();
+
+        /*!
+         * Returns the XiphComment for this file.  XiphComment implements the tag
+         * interface, so this serves as the reimplementation of
+         * TagLib::File::tag().
+         */
+        virtual Ogg::XiphComment *tag() const;
+
+        /*!
+         * Returns the Speex::Properties for this file.  If no audio properties
+         * were read then this will return a null pointer.
+         */
+        virtual Properties *audioProperties() const;
+
+        virtual bool save();
+
+      private:
+        File(const File &);
+        File &operator=(const File &);
+
+        void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+        class FilePrivate;
+        FilePrivate *d;
+      };
+    }
+  }
+}
+
+#endif
diff --git a/src/taglib/ogg/speex/speexproperties.cpp b/src/taglib/ogg/speex/speexproperties.cpp
new file mode 100644 (file)
index 0000000..29deb01
--- /dev/null
@@ -0,0 +1,170 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+                           (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include <oggpageheader.h>
+
+#include "speexproperties.h"
+#include "speexfile.h"
+
+using namespace TagLib;
+using namespace TagLib::Ogg;
+
+class Speex::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(File *f, ReadStyle s) :
+    file(f),
+    style(s),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0),
+    speexVersion(0),
+    vbr(false),
+    mode(0) {}
+
+  File *file;
+  ReadStyle style;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+  int speexVersion;
+  bool vbr;
+  int mode;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Speex::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(file, style);
+  read();
+}
+
+Speex::Properties::~Properties()
+{
+  delete d;
+}
+
+int Speex::Properties::length() const
+{
+  return d->length;
+}
+
+int Speex::Properties::bitrate() const
+{
+  return int(float(d->bitrate) / float(1000) + 0.5);
+}
+
+int Speex::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int Speex::Properties::channels() const
+{
+  return d->channels;
+}
+
+int Speex::Properties::speexVersion() const
+{
+  return d->speexVersion;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Speex::Properties::read()
+{
+  // Get the identification header from the Ogg implementation.
+
+  ByteVector data = d->file->packet(0);
+
+  int pos = 28;
+
+  // speex_version_id;       /**< Version for Speex (for checking compatibility) */
+  d->speexVersion = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  // header_size;            /**< Total size of the header ( sizeof(SpeexHeader) ) */
+  pos += 4;
+
+  // rate;                   /**< Sampling rate used */
+  d->sampleRate = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  // mode;                   /**< Mode used (0 for narrowband, 1 for wideband) */
+  d->mode = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  // mode_bitstream_version; /**< Version ID of the bit-stream */
+  pos += 4;
+
+  // nb_channels;            /**< Number of channels encoded */
+  d->channels = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  // bitrate;                /**< Bit-rate used */
+  d->bitrate = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  // frame_size;             /**< Size of frames */
+  // unsigned int frameSize = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  // vbr;                    /**< 1 for a VBR encoding, 0 otherwise */
+  d->vbr = data.mid(pos, 4).toUInt(false) == 1;
+  pos += 4;
+
+  // frames_per_packet;      /**< Number of frames stored per Ogg packet */
+  // unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false);
+
+  const Ogg::PageHeader *first = d->file->firstPageHeader();
+  const Ogg::PageHeader *last = d->file->lastPageHeader();
+
+  if(first && last) {
+    long long start = first->absoluteGranularPosition();
+    long long end = last->absoluteGranularPosition();
+
+    if(start >= 0 && end >= 0 && d->sampleRate > 0)
+      d->length = (int) ((end - start) / (long long) d->sampleRate);
+    else
+      debug("Speex::Properties::read() -- Either the PCM values for the start or "
+            "end of this file was incorrect or the sample rate is zero.");
+  }
+  else
+    debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages.");
+}
diff --git a/src/taglib/ogg/speex/speexproperties.h b/src/taglib/ogg/speex/speexproperties.h
new file mode 100644 (file)
index 0000000..081f0e0
--- /dev/null
@@ -0,0 +1,89 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+                           (original Vorbis implementation)
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_SPEEXPROPERTIES_H
+#define TAGLIB_SPEEXPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace Ogg {
+
+    namespace Speex {
+
+      class File;
+
+      //! An implementation of audio property reading for Ogg Speex
+
+      /*!
+       * This reads the data from an Ogg Speex stream found in the AudioProperties
+       * API.
+       */
+
+      class TAGLIB_EXPORT Properties : public AudioProperties
+      {
+      public:
+        /*!
+         * Create an instance of Speex::Properties with the data read from the
+         * Speex::File \a file.
+         */
+        Properties(File *file, ReadStyle style = Average);
+
+        /*!
+         * Destroys this Speex::Properties instance.
+         */
+        virtual ~Properties();
+
+        // Reimplementations.
+
+        virtual int length() const;
+        virtual int bitrate() const;
+        virtual int sampleRate() const;
+        virtual int channels() const;
+
+        /*!
+         * Returns the Speex version, currently "0" (as specified by the spec).
+         */
+        int speexVersion() const;
+
+      private:
+        Properties(const Properties &);
+        Properties &operator=(const Properties &);
+
+        void read();
+
+        class PropertiesPrivate;
+        PropertiesPrivate *d;
+      };
+    }
+  }
+}
+
+#endif
diff --git a/src/taglib/ogg/vorbis/vorbisfile.cpp b/src/taglib/ogg/vorbis/vorbisfile.cpp
new file mode 100644 (file)
index 0000000..114166a
--- /dev/null
@@ -0,0 +1,117 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "vorbisfile.h"
+
+using namespace TagLib;
+
+class Vorbis::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    comment(0),
+    properties(0) {}
+
+  ~FilePrivate()
+  {
+    delete comment;
+    delete properties;
+  }
+
+  Ogg::XiphComment *comment;
+  Properties *properties;
+};
+
+namespace TagLib {
+  /*!
+   * Vorbis headers can be found with one type ID byte and the string "vorbis" in
+   * an Ogg stream.  0x03 indicates the comment header.
+   */
+  static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Vorbis::File::File(FileName file, bool readProperties,
+                   Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+Vorbis::File::~File()
+{
+  delete d;
+}
+
+Ogg::XiphComment *Vorbis::File::tag() const
+{
+  return d->comment;
+}
+
+Vorbis::Properties *Vorbis::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool Vorbis::File::save()
+{
+  ByteVector v(vorbisCommentHeaderID);
+
+  if(!d->comment)
+    d->comment = new Ogg::XiphComment;
+  v.append(d->comment->render());
+
+  setPacket(1, v);
+
+  return Ogg::File::save();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Vorbis::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  ByteVector commentHeaderData = packet(1);
+
+  if(commentHeaderData.mid(0, 7) != vorbisCommentHeaderID) {
+    debug("Vorbis::File::read() - Could not find the Vorbis comment header.");
+    setValid(false);
+    return;
+  }
+
+  d->comment = new Ogg::XiphComment(commentHeaderData.mid(7));
+
+  if(readProperties)
+    d->properties = new Properties(this, propertiesStyle);
+}
diff --git a/src/taglib/ogg/vorbis/vorbisfile.h b/src/taglib/ogg/vorbis/vorbisfile.h
new file mode 100644 (file)
index 0000000..9eb5ac6
--- /dev/null
@@ -0,0 +1,118 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_VORBISFILE_H
+#define TAGLIB_VORBISFILE_H
+
+#include "taglib_export.h"
+#include "oggfile.h"
+#include "xiphcomment.h"
+
+#include "vorbisproperties.h"
+
+namespace TagLib {
+
+/*
+ * This is just to make this appear to be in the Ogg namespace in the
+ * documentation.  The typedef below will make this work with the current code.
+ * In the next BIC version of TagLib this will be really moved into the Ogg
+ * namespace.
+ */
+
+#ifdef DOXYGEN
+  namespace Ogg {
+#endif
+
+  //! A namespace containing classes for Vorbis metadata
+
+  namespace Vorbis {
+
+
+    //! An implementation of Ogg::File with Vorbis specific methods
+
+    /*!
+     * This is the central class in the Ogg Vorbis metadata processing collection
+     * of classes.  It's built upon Ogg::File which handles processing of the Ogg
+     * logical bitstream and breaking it down into pages which are handled by
+     * the codec implementations, in this case Vorbis specifically.
+     */
+
+    class TAGLIB_EXPORT File : public Ogg::File
+    {
+    public:
+      /*!
+       * Contructs a Vorbis file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       */
+      File(FileName file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns the XiphComment for this file.  XiphComment implements the tag
+       * interface, so this serves as the reimplementation of
+       * TagLib::File::tag().
+       */
+      virtual Ogg::XiphComment *tag() const;
+
+      /*!
+       * Returns the Vorbis::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      virtual bool save();
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+
+/*
+ * To keep compatibility with the current version put Vorbis in the Ogg namespace
+ * only in the docs and provide a typedef to make it work.  In the next BIC
+ * version this will be removed and it will only exist in the Ogg namespace.
+ */
+
+#ifdef DOXYGEN
+  }
+#else
+  namespace Ogg { namespace Vorbis { typedef TagLib::Vorbis::File File; } }
+#endif
+
+}
+
+#endif
diff --git a/src/taglib/ogg/vorbis/vorbisproperties.cpp b/src/taglib/ogg/vorbis/vorbisproperties.cpp
new file mode 100644 (file)
index 0000000..c82a319
--- /dev/null
@@ -0,0 +1,183 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include <oggpageheader.h>
+
+#include "vorbisproperties.h"
+#include "vorbisfile.h"
+
+using namespace TagLib;
+
+class Vorbis::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(File *f, ReadStyle s) :
+    file(f),
+    style(s),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0),
+    vorbisVersion(0),
+    bitrateMaximum(0),
+    bitrateNominal(0),
+    bitrateMinimum(0) {}
+
+  File *file;
+  ReadStyle style;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+  int vorbisVersion;
+  int bitrateMaximum;
+  int bitrateNominal;
+  int bitrateMinimum;
+};
+
+namespace TagLib {
+  /*!
+   * Vorbis headers can be found with one type ID byte and the string "vorbis" in
+   * an Ogg stream.  0x01 indicates the setup header.
+   */
+  static const char vorbisSetupHeaderID[] = { 0x01, 'v', 'o', 'r', 'b', 'i', 's', 0 };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Vorbis::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(file, style);
+  read();
+}
+
+Vorbis::Properties::~Properties()
+{
+  delete d;
+}
+
+int Vorbis::Properties::length() const
+{
+  return d->length;
+}
+
+int Vorbis::Properties::bitrate() const
+{
+  return int(float(d->bitrate) / float(1000) + 0.5);
+}
+
+int Vorbis::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int Vorbis::Properties::channels() const
+{
+  return d->channels;
+}
+
+int Vorbis::Properties::vorbisVersion() const
+{
+  return d->vorbisVersion;
+}
+
+int Vorbis::Properties::bitrateMaximum() const
+{
+  return d->bitrateMaximum;
+}
+
+int Vorbis::Properties::bitrateNominal() const
+{
+  return d->bitrateNominal;
+}
+
+int Vorbis::Properties::bitrateMinimum() const
+{
+  return d->bitrateMinimum;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Vorbis::Properties::read()
+{
+  // Get the identification header from the Ogg implementation.
+
+  ByteVector data = d->file->packet(0);
+
+  int pos = 0;
+
+  if(data.mid(pos, 7) != vorbisSetupHeaderID) {
+    debug("Vorbis::Properties::read() -- invalid Vorbis identification header");
+    return;
+  }
+
+  pos += 7;
+
+  d->vorbisVersion = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  d->channels = uchar(data[pos]);
+  pos += 1;
+
+  d->sampleRate = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  d->bitrateMaximum = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  d->bitrateNominal = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  d->bitrateMinimum = data.mid(pos, 4).toUInt(false);
+
+  // TODO: Later this should be only the "fast" mode.
+  d->bitrate = d->bitrateNominal;
+
+  // Find the length of the file.  See http://wiki.xiph.org/VorbisStreamLength/
+  // for my notes on the topic.
+
+  const Ogg::PageHeader *first = d->file->firstPageHeader();
+  const Ogg::PageHeader *last = d->file->lastPageHeader();
+
+  if(first && last) {
+    long long start = first->absoluteGranularPosition();
+    long long end = last->absoluteGranularPosition();
+
+    if(start >= 0 && end >= 0 && d->sampleRate > 0)
+      d->length = (end - start) / (long long) d->sampleRate;
+    else
+      debug("Vorbis::Properties::read() -- Either the PCM values for the start or "
+            "end of this file was incorrect or the sample rate is zero.");
+  }
+  else
+    debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages.");
+}
diff --git a/src/taglib/ogg/vorbis/vorbisproperties.h b/src/taglib/ogg/vorbis/vorbisproperties.h
new file mode 100644 (file)
index 0000000..80804a2
--- /dev/null
@@ -0,0 +1,125 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_VORBISPROPERTIES_H
+#define TAGLIB_VORBISPROPERTIES_H
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+namespace TagLib {
+
+/*
+ * This is just to make this appear to be in the Ogg namespace in the
+ * documentation.  The typedef below will make this work with the current code.
+ * In the next BIC version of TagLib this will be really moved into the Ogg
+ * namespace.
+ */
+
+#ifdef DOXYGEN
+  namespace Ogg {
+#endif
+
+  namespace Vorbis {
+
+    class File;
+
+    //! An implementation of audio property reading for Ogg Vorbis
+
+    /*!
+     * This reads the data from an Ogg Vorbis stream found in the AudioProperties
+     * API.
+     */
+
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      /*!
+       * Create an instance of Vorbis::Properties with the data read from the
+       * Vorbis::File \a file.
+       */
+      Properties(File *file, ReadStyle style = Average);
+
+      /*!
+       * Destroys this VorbisProperties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+      /*!
+       * Returns the Vorbis version, currently "0" (as specified by the spec).
+       */
+      int vorbisVersion() const;
+
+      /*!
+       * Returns the maximum bitrate as read from the Vorbis identification
+       * header.
+       */
+      int bitrateMaximum() const;
+
+      /*!
+       * Returns the nominal bitrate as read from the Vorbis identification
+       * header.
+       */
+      int bitrateNominal() const;
+
+      /*!
+       * Returns the minimum bitrate as read from the Vorbis identification
+       * header.
+       */
+      int bitrateMinimum() const;
+
+    private:
+      Properties(const Properties &);
+      Properties &operator=(const Properties &);
+
+      void read();
+
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+  }
+
+/*
+ * To keep compatibility with the current version put Vorbis in the Ogg namespace
+ * only in the docs and provide a typedef to make it work.  In the next BIC
+ * version this will be removed and it will only exist in the Ogg namespace.
+ */
+
+#ifdef DOXYGEN
+  }
+#else
+  namespace Ogg { namespace Vorbis { typedef TagLib::AudioProperties AudioProperties; } }
+#endif
+
+}
+
+#endif
diff --git a/src/taglib/ogg/xiphcomment.cpp b/src/taglib/ogg/xiphcomment.cpp
new file mode 100644 (file)
index 0000000..406137a
--- /dev/null
@@ -0,0 +1,319 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tdebug.h>
+
+#include <xiphcomment.h>
+
+using namespace TagLib;
+
+class Ogg::XiphComment::XiphCommentPrivate
+{
+public:
+  FieldListMap fieldListMap;
+  String vendorID;
+  String commentField;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::XiphComment::XiphComment() : TagLib::Tag()
+{
+  d = new XiphCommentPrivate;
+}
+
+Ogg::XiphComment::XiphComment(const ByteVector &data) : TagLib::Tag()
+{
+  d = new XiphCommentPrivate;
+  parse(data);
+}
+
+Ogg::XiphComment::~XiphComment()
+{
+  delete d;
+}
+
+String Ogg::XiphComment::title() const
+{
+  if(d->fieldListMap["TITLE"].isEmpty())
+    return String::null;
+  return d->fieldListMap["TITLE"].front();
+}
+
+String Ogg::XiphComment::artist() const
+{
+  if(d->fieldListMap["ARTIST"].isEmpty())
+    return String::null;
+  return d->fieldListMap["ARTIST"].front();
+}
+
+String Ogg::XiphComment::album() const
+{
+  if(d->fieldListMap["ALBUM"].isEmpty())
+    return String::null;
+  return d->fieldListMap["ALBUM"].front();
+}
+
+String Ogg::XiphComment::comment() const
+{
+  if(!d->fieldListMap["DESCRIPTION"].isEmpty()) {
+    d->commentField = "DESCRIPTION";
+    return d->fieldListMap["DESCRIPTION"].front();
+  }
+
+  if(!d->fieldListMap["COMMENT"].isEmpty()) {
+    d->commentField = "COMMENT";
+    return d->fieldListMap["COMMENT"].front();
+  }
+
+  return String::null;
+}
+
+String Ogg::XiphComment::genre() const
+{
+  if(d->fieldListMap["GENRE"].isEmpty())
+    return String::null;
+  return d->fieldListMap["GENRE"].front();
+}
+
+TagLib::uint Ogg::XiphComment::year() const
+{
+  if(!d->fieldListMap["DATE"].isEmpty())
+    return d->fieldListMap["DATE"].front().toInt();
+  if(!d->fieldListMap["YEAR"].isEmpty())
+    return d->fieldListMap["YEAR"].front().toInt();
+  return 0;
+}
+
+TagLib::uint Ogg::XiphComment::track() const
+{
+  if(!d->fieldListMap["TRACKNUMBER"].isEmpty())
+    return d->fieldListMap["TRACKNUMBER"].front().toInt();
+  if(!d->fieldListMap["TRACKNUM"].isEmpty())
+    return d->fieldListMap["TRACKNUM"].front().toInt();
+  return 0;
+}
+
+void Ogg::XiphComment::setTitle(const String &s)
+{
+  addField("TITLE", s);
+}
+
+void Ogg::XiphComment::setArtist(const String &s)
+{
+  addField("ARTIST", s);
+}
+
+void Ogg::XiphComment::setAlbum(const String &s)
+{
+  addField("ALBUM", s);
+}
+
+void Ogg::XiphComment::setComment(const String &s)
+{
+  addField(d->commentField.isEmpty() ? "DESCRIPTION" : d->commentField, s);
+}
+
+void Ogg::XiphComment::setGenre(const String &s)
+{
+  addField("GENRE", s);
+}
+
+void Ogg::XiphComment::setYear(uint i)
+{
+  removeField("YEAR");
+  if(i == 0)
+    removeField("DATE");
+  else
+    addField("DATE", String::number(i));
+}
+
+void Ogg::XiphComment::setTrack(uint i)
+{
+  removeField("TRACKNUM");
+  if(i == 0)
+    removeField("TRACKNUMBER");
+  else
+    addField("TRACKNUMBER", String::number(i));
+}
+
+bool Ogg::XiphComment::isEmpty() const
+{
+  FieldListMap::ConstIterator it = d->fieldListMap.begin();
+  for(; it != d->fieldListMap.end(); ++it)
+    if(!(*it).second.isEmpty())
+      return false;
+
+  return true;
+}
+
+TagLib::uint Ogg::XiphComment::fieldCount() const
+{
+  uint count = 0;
+
+  FieldListMap::ConstIterator it = d->fieldListMap.begin();
+  for(; it != d->fieldListMap.end(); ++it)
+    count += (*it).second.size();
+
+  return count;
+}
+
+const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
+{
+  return d->fieldListMap;
+}
+
+String Ogg::XiphComment::vendorID() const
+{
+  return d->vendorID;
+}
+
+void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
+{
+  if(replace)
+    removeField(key.upper());
+
+  if(!key.isEmpty() && !value.isEmpty())
+    d->fieldListMap[key.upper()].append(value);
+}
+
+void Ogg::XiphComment::removeField(const String &key, const String &value)
+{
+  if(!value.isNull()) {
+    StringList::Iterator it = d->fieldListMap[key].begin();
+    while(it != d->fieldListMap[key].end()) {
+      if(value == *it)
+        it = d->fieldListMap[key].erase(it);
+      else
+        it++;
+    }
+  }
+  else
+    d->fieldListMap.erase(key);
+}
+
+bool Ogg::XiphComment::contains(const String &key) const
+{
+  return d->fieldListMap.contains(key) && !d->fieldListMap[key].isEmpty();
+}
+
+ByteVector Ogg::XiphComment::render() const
+{
+  return render(true);
+}
+
+ByteVector Ogg::XiphComment::render(bool addFramingBit) const
+{
+  ByteVector data;
+
+  // Add the vendor ID length and the vendor ID.  It's important to use the
+  // length of the data(String::UTF8) rather than the length of the the string
+  // since this is UTF8 text and there may be more characters in the data than
+  // in the UTF16 string.
+
+  ByteVector vendorData = d->vendorID.data(String::UTF8);
+
+  data.append(ByteVector::fromUInt(vendorData.size(), false));
+  data.append(vendorData);
+
+  // Add the number of fields.
+
+  data.append(ByteVector::fromUInt(fieldCount(), false));
+
+  // Iterate over the the field lists.  Our iterator returns a
+  // std::pair<String, StringList> where the first String is the field name and
+  // the StringList is the values associated with that field.
+
+  FieldListMap::ConstIterator it = d->fieldListMap.begin();
+  for(; it != d->fieldListMap.end(); ++it) {
+
+    // And now iterate over the values of the current list.
+
+    String fieldName = (*it).first;
+    StringList values = (*it).second;
+
+    StringList::ConstIterator valuesIt = values.begin();
+    for(; valuesIt != values.end(); ++valuesIt) {
+      ByteVector fieldData = fieldName.data(String::UTF8);
+      fieldData.append('=');
+      fieldData.append((*valuesIt).data(String::UTF8));
+
+      data.append(ByteVector::fromUInt(fieldData.size(), false));
+      data.append(fieldData);
+    }
+  }
+
+  // Append the "framing bit".
+
+  if(addFramingBit)
+    data.append(char(1));
+
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void Ogg::XiphComment::parse(const ByteVector &data)
+{
+  // The first thing in the comment data is the vendor ID length, followed by a
+  // UTF8 string with the vendor ID.
+
+  int pos = 0;
+
+  int vendorLength = data.mid(0, 4).toUInt(false);
+  pos += 4;
+
+  d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
+  pos += vendorLength;
+
+  // Next the number of fields in the comment vector.
+
+  int commentFields = data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  for(int i = 0; i < commentFields; i++) {
+
+    // Each comment field is in the format "KEY=value" in a UTF8 string and has
+    // 4 bytes before the text starts that gives the length.
+
+    int commentLength = data.mid(pos, 4).toUInt(false);
+    pos += 4;
+
+    String comment = String(data.mid(pos, commentLength), String::UTF8);
+    pos += commentLength;
+
+    int commentSeparatorPosition = comment.find("=");
+
+    String key = comment.substr(0, commentSeparatorPosition);
+    String value = comment.substr(commentSeparatorPosition + 1);
+
+    addField(key, value, false);
+  }
+}
diff --git a/src/taglib/ogg/xiphcomment.h b/src/taglib/ogg/xiphcomment.h
new file mode 100644 (file)
index 0000000..818b3f4
--- /dev/null
@@ -0,0 +1,202 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_VORBISCOMMENT_H
+#define TAGLIB_VORBISCOMMENT_H
+
+#include "tag.h"
+#include "tlist.h"
+#include "tmap.h"
+#include "tstring.h"
+#include "tstringlist.h"
+#include "tbytevector.h"
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace Ogg {
+
+    /*!
+     * A mapping between a list of field names, or keys, and a list of values
+     * associated with that field.
+     *
+     * \see XiphComment::fieldListMap()
+     */
+    typedef Map<String, StringList> FieldListMap;
+
+    //! Ogg Vorbis comment implementation
+
+    /*!
+     * This class is an implementation of the Ogg Vorbis comment specification,
+     * to be found in section 5 of the Ogg Vorbis specification.  Because this
+     * format is also used in other (currently unsupported) Xiph.org formats, it
+     * has been made part of a generic implementation rather than being limited
+     * to strictly Vorbis.
+     *
+     * Vorbis comments are a simple vector of keys and values, called fields.
+     * Multiple values for a given key are supported.
+     *
+     * \see fieldListMap()
+     */
+
+    class TAGLIB_EXPORT XiphComment : public TagLib::Tag
+    {
+    public:
+      /*!
+       * Constructs an empty Vorbis comment.
+       */
+      XiphComment();
+
+      /*!
+       * Constructs a Vorbis comment from \a data.
+       */
+      XiphComment(const ByteVector &data);
+
+      /*!
+       * Destroys this instance of the XiphComment.
+       */
+      virtual ~XiphComment();
+
+      virtual String title() const;
+      virtual String artist() const;
+      virtual String album() const;
+      virtual String comment() const;
+      virtual String genre() const;
+      virtual uint year() const;
+      virtual uint track() const;
+
+      virtual void setTitle(const String &s);
+      virtual void setArtist(const String &s);
+      virtual void setAlbum(const String &s);
+      virtual void setComment(const String &s);
+      virtual void setGenre(const String &s);
+      virtual void setYear(uint i);
+      virtual void setTrack(uint i);
+
+      virtual bool isEmpty() const;
+
+      /*!
+       * Returns the number of fields present in the comment.
+       */
+      uint fieldCount() const;
+
+      /*!
+       * Returns a reference to the map of field lists.  Because Xiph comments
+       * support multiple fields with the same key, a pure Map would not work.
+       * As such this is a Map of string lists, keyed on the comment field name.
+       *
+       * The standard set of Xiph/Vorbis fields (which may or may not be
+       * contained in any specific comment) is:
+       *
+       * <ul>
+       *   <li>TITLE</li>
+       *   <li>VERSION</li>
+       *   <li>ALBUM</li>
+       *   <li>ARTIST</li>
+       *   <li>PERFORMER</li>
+       *   <li>COPYRIGHT</li>
+       *   <li>ORGANIZATION</li>
+       *   <li>DESCRIPTION</li>
+       *   <li>GENRE</li>
+       *   <li>DATE</li>
+       *   <li>LOCATION</li>
+       *   <li>CONTACT</li>
+       *   <li>ISRC</li>
+       * </ul>
+       *
+       * For a more detailed description of these fields, please see the Ogg
+       * Vorbis specification, section 5.2.2.1.
+       *
+       * \note The Ogg Vorbis comment specification does allow these key values
+       * to be either upper or lower case.  However, it is conventional for them
+       * to be upper case.  As such, TagLib, when parsing a Xiph/Vorbis comment,
+       * converts all fields to uppercase.  When you are using this data
+       * structure, you will need to specify the field name in upper case.
+       *
+       * \warning You should not modify this data structure directly, instead
+       * use addField() and removeField().
+       */
+      const FieldListMap &fieldListMap() const;
+
+      /*!
+       * Returns the vendor ID of the Ogg Vorbis encoder.  libvorbis 1.0 as the
+       * most common case always returns "Xiph.Org libVorbis I 20020717".
+       */
+      String vendorID() const;
+
+      /*!
+       * Add the field specified by \a key with the data \a value.  If \a replace
+       * is true, then all of the other fields with the same key will be removed
+       * first.
+       *
+       * If the field value is empty, the field will be removed.
+       */
+      void addField(const String &key, const String &value, bool replace = true);
+
+      /*!
+       * Remove the field specified by \a key with the data \a value.  If
+       * \a value is null, all of the fields with the given key will be removed.
+       */
+      void removeField(const String &key, const String &value = String::null);
+
+      /*!
+       * Returns true if the field is contained within the comment.
+       *
+       * \note This is safer than checking for membership in the FieldListMap.
+       */
+      bool contains(const String &key) const;
+
+      /*!
+       * Renders the comment to a ByteVector suitable for inserting into a file.
+       */
+      ByteVector render() const; // BIC: remove and merge with below
+
+      /*!
+       * Renders the comment to a ByteVector suitable for inserting into a file.
+       *
+       * If \a addFramingBit is true the standard Vorbis comment framing bit will
+       * be appended.  However some formats (notably FLAC) do not work with this
+       * in place.
+       */
+      ByteVector render(bool addFramingBit) const;
+
+    protected:
+      /*!
+       * Reads the tag from the file specified in the constructor and fills the
+       * FieldListMap.
+       */
+      void parse(const ByteVector &data);
+
+    private:
+      XiphComment(const XiphComment &);
+      XiphComment &operator=(const XiphComment &);
+
+      class XiphCommentPrivate;
+      XiphCommentPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/riff/aiff/aifffile.cpp b/src/taglib/riff/aiff/aifffile.cpp
new file mode 100644 (file)
index 0000000..9bc5cd1
--- /dev/null
@@ -0,0 +1,108 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tdebug.h>
+#include <id3v2tag.h>
+
+#include "aifffile.h"
+
+using namespace TagLib;
+
+class RIFF::AIFF::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    properties(0),
+    tag(0)
+  {
+
+  }
+
+  ~FilePrivate()
+  {
+    delete properties;
+    delete tag;
+  }
+
+  Properties *properties;
+  ID3v2::Tag *tag;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+RIFF::AIFF::File::File(FileName file, bool readProperties,
+                       Properties::ReadStyle propertiesStyle) : RIFF::File(file, BigEndian)
+{
+  d = new FilePrivate;
+  if(isOpen())
+    read(readProperties, propertiesStyle);
+}
+
+RIFF::AIFF::File::~File()
+{
+  delete d;
+}
+
+ID3v2::Tag *RIFF::AIFF::File::tag() const
+{
+  return d->tag;
+}
+
+RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool RIFF::AIFF::File::save()
+{
+  if(readOnly()) {
+    debug("RIFF::AIFF::File::save() -- File is read only.");
+    return false;
+  }
+
+  setChunkData("ID3 ", d->tag->render());
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void RIFF::AIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  for(uint i = 0; i < chunkCount(); i++) {
+    if(chunkName(i) == "ID3 ")
+      d->tag = new ID3v2::Tag(this, chunkOffset(i));
+    else if(chunkName(i) == "COMM" && readProperties)
+      d->properties = new Properties(chunkData(i), propertiesStyle);
+  }
+
+  if(!d->tag)
+    d->tag = new ID3v2::Tag;
+}
diff --git a/src/taglib/riff/aiff/aifffile.h b/src/taglib/riff/aiff/aifffile.h
new file mode 100644 (file)
index 0000000..533d83d
--- /dev/null
@@ -0,0 +1,102 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_AIFFFILE_H
+#define TAGLIB_AIFFFILE_H
+
+#include "rifffile.h"
+#include "id3v2tag.h"
+#include "aiffproperties.h"
+
+namespace TagLib {
+
+  namespace RIFF {
+
+    //! An implementation of AIFF metadata
+
+    /*!
+     * This is implementation of AIFF metadata.
+     *
+     * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
+     * chunk as well as properties from the file.
+     */
+
+    namespace AIFF {
+
+      //! An implementation of TagLib::File with AIFF specific methods
+
+      /*!
+       * This implements and provides an interface for AIFF files to the
+       * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+       * the abstract TagLib::File API as well as providing some additional
+       * information specific to AIFF files.
+       */
+
+      class TAGLIB_EXPORT File : public TagLib::RIFF::File
+      {
+      public:
+        /*!
+         * Contructs an AIFF file from \a file.  If \a readProperties is true the
+         * file's audio properties will also be read using \a propertiesStyle.  If
+         * false, \a propertiesStyle is ignored.
+         */
+        File(FileName file, bool readProperties = true,
+             Properties::ReadStyle propertiesStyle = Properties::Average);
+
+        /*!
+         * Destroys this instance of the File.
+         */
+        virtual ~File();
+
+        /*!
+         * Returns the Tag for this file.
+         */
+        virtual ID3v2::Tag *tag() const;
+
+        /*!
+         * Returns the AIFF::Properties for this file.  If no audio properties
+         * were read then this will return a null pointer.
+         */
+        virtual Properties *audioProperties() const;
+
+        /*!
+         * Saves the file.
+         */
+        virtual bool save();
+
+      private:
+        File(const File &);
+        File &operator=(const File &);
+
+        void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+        class FilePrivate;
+        FilePrivate *d;
+      };
+    }
+  }
+}
+
+#endif
diff --git a/src/taglib/riff/aiff/aiffproperties.cpp b/src/taglib/riff/aiff/aiffproperties.cpp
new file mode 100644 (file)
index 0000000..e60d936
--- /dev/null
@@ -0,0 +1,146 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <cmath>
+// ldexp is a c99 function, which might not be defined in <cmath>
+// so we pull in math.h too and hope it does the right (wrong) thing
+// wrt. c99 functions in C++
+#include <math.h>
+
+#include "aiffproperties.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// nasty 80-bit float helpers
+////////////////////////////////////////////////////////////////////////////////
+
+#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)
+
+static double ConvertFromIeeeExtended(unsigned char *bytes)
+{
+  double f;
+  int expon;
+  unsigned long hiMant, loMant;
+    
+  expon  = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
+
+  hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) |
+           ((unsigned long)(bytes[3] & 0xFF) << 16) |
+           ((unsigned long)(bytes[4] & 0xFF) << 8)  |
+           ((unsigned long)(bytes[5] & 0xFF));
+
+  loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) |
+           ((unsigned long)(bytes[7] & 0xFF) << 16) |
+           ((unsigned long)(bytes[8] & 0xFF) << 8)  |
+           ((unsigned long)(bytes[9] & 0xFF));
+
+  if (expon == 0 && hiMant == 0 && loMant == 0)
+    f = 0;
+  else {
+    if(expon == 0x7FFF) /* Infinity or NaN */
+      f = HUGE_VAL;
+    else {
+      expon -= 16383;
+      f  = ldexp(UnsignedToFloat(hiMant), expon -= 31);
+      f += ldexp(UnsignedToFloat(loMant), expon -= 32);
+    }
+  }
+
+  if(bytes[0] & 0x80)
+    return -f;
+  else
+    return f;
+}
+
+using namespace TagLib;
+
+class RIFF::AIFF::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate() :
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0)
+  {
+
+  }
+
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+RIFF::AIFF::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate;
+  read(data);
+}
+
+RIFF::AIFF::Properties::~Properties()
+{
+  delete d;
+}
+
+int RIFF::AIFF::Properties::length() const
+{
+  return d->length;
+}
+
+int RIFF::AIFF::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int RIFF::AIFF::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int RIFF::AIFF::Properties::channels() const
+{
+  return d->channels;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void RIFF::AIFF::Properties::read(const ByteVector &data)
+{
+  d->channels       = data.mid(0, 2).toShort();
+  uint sampleFrames = data.mid(2, 4).toUInt();
+  short sampleSize  = data.mid(6, 2).toShort();
+  double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<unsigned char *>(data.mid(8, 10).data()));
+  d->sampleRate     = sampleRate;
+  d->bitrate        = (sampleRate * sampleSize * d->channels) / 1024.0;
+  d->length         = sampleFrames / d->sampleRate;
+}
diff --git a/src/taglib/riff/aiff/aiffproperties.h b/src/taglib/riff/aiff/aiffproperties.h
new file mode 100644 (file)
index 0000000..1408cb3
--- /dev/null
@@ -0,0 +1,80 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_AIFFPROPERTIES_H
+#define TAGLIB_AIFFPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace RIFF {
+
+    namespace AIFF {
+
+      class File;
+
+      //! An implementation of audio property reading for AIFF
+
+      /*!
+       * This reads the data from an AIFF stream found in the AudioProperties
+       * API.
+       */
+
+      class TAGLIB_EXPORT Properties : public AudioProperties
+      {
+      public:
+       /*!
+        * Create an instance of AIFF::Properties with the data read from the
+        * ByteVector \a data.
+        */
+       Properties(const ByteVector &data, ReadStyle style);
+
+       /*!
+        * Destroys this AIFF::Properties instance.
+        */
+       virtual ~Properties();
+
+       // Reimplementations.
+
+       virtual int length() const;
+       virtual int bitrate() const;
+       virtual int sampleRate() const;
+       virtual int channels() const;
+
+      private:
+       Properties(const Properties &);
+       Properties &operator=(const Properties &);
+
+       void read(const ByteVector &data);
+
+       class PropertiesPrivate;
+       PropertiesPrivate *d;
+      };
+    }
+  }
+}
+
+#endif
diff --git a/src/taglib/riff/rifffile.cpp b/src/taglib/riff/rifffile.cpp
new file mode 100644 (file)
index 0000000..595691f
--- /dev/null
@@ -0,0 +1,211 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tdebug.h>
+#include <tstring.h>
+
+#include "rifffile.h"
+#include <vector>
+
+using namespace TagLib;
+
+class RIFF::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    endianness(BigEndian),
+    size(0)
+  {
+
+  }
+  Endianness endianness;
+  ByteVector type;
+  uint size;
+  ByteVector format;
+
+  std::vector<ByteVector> chunkNames;
+  std::vector<uint> chunkOffsets;
+  std::vector<uint> chunkSizes;
+  std::vector<char> chunkPadding;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+RIFF::File::~File()
+{
+  delete d;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+RIFF::File::File(FileName file, Endianness endianness) : TagLib::File(file)
+{
+  d = new FilePrivate;
+  d->endianness = endianness;
+
+  if(isOpen())
+    read();
+}
+
+TagLib::uint RIFF::File::chunkCount() const
+{
+  return d->chunkNames.size();
+}
+
+TagLib::uint RIFF::File::chunkOffset(uint i) const
+{
+  return d->chunkOffsets[i];
+}
+
+ByteVector RIFF::File::chunkName(uint i) const
+{
+  if(i >= chunkCount())
+    return ByteVector::null;
+
+  return d->chunkNames[i];
+}
+
+ByteVector RIFF::File::chunkData(uint i)
+{
+  if(i >= chunkCount())
+    return ByteVector::null;
+
+  // Offset for the first subchunk's data
+
+  long begin = 12 + 8;
+
+  for(uint it = 0; it < i; it++)
+    begin += 8 + d->chunkSizes[it] + d->chunkPadding[it];
+
+  seek(begin);
+
+  return readBlock(d->chunkSizes[i]);
+}
+
+void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
+{
+  if(d->chunkNames.size() == 0)
+  {
+    debug("RIFF::File::setChunkData - No valid chunks found.");
+    return;
+  }
+
+  for(uint i = 0; i < d->chunkNames.size(); i++) {
+    if(d->chunkNames[i] == name) {
+
+      int sizeDifference = data.size() - d->chunkSizes[i];
+
+      // First we update the global size
+
+      insert(ByteVector::fromUInt(d->size + sizeDifference,
+                                  d->endianness == BigEndian), 4, 4);
+
+      // Now update the specific chunk
+
+      writeChunk(name, data, d->chunkOffsets[i] - 8, d->chunkSizes[i] + d->chunkPadding[i] + 8);
+
+      d->chunkSizes[i] = data.size();
+      d->chunkPadding[i] = (data.size() & 0x01) ? 1 : 0;
+
+      // Now update the internal offsets
+
+      for(i++; i < d->chunkNames.size(); i++)
+        d->chunkOffsets[i] = d->chunkOffsets[i-1] + 8 + d->chunkSizes[i-1] + d->chunkPadding[i-1];
+
+      return;
+    }
+  }
+
+  // Couldn't find an existing chunk, so let's create a new one.  First update
+  // the global size:
+
+  insert(ByteVector::fromUInt(d->size + data.size() + 8, d->endianness == BigEndian), 4, 4);
+  writeChunk(name, data, d->chunkOffsets.back() + d->chunkSizes.back());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void RIFF::File::read()
+{
+  bool bigEndian = (d->endianness == BigEndian);
+
+  d->type = readBlock(4);
+  d->size = readBlock(4).toUInt(bigEndian);
+  d->format = readBlock(4);
+
+  // + 8: chunk header at least, fix for additional junk bytes
+  while(tell() + 8 <= length()) {
+    ByteVector chunkName = readBlock(4);
+    uint chunkSize = readBlock(4).toUInt(bigEndian);
+
+    if(tell() + chunkSize > uint(length())) {
+      // something wrong
+      break;
+    }
+
+    d->chunkNames.push_back(chunkName);
+    d->chunkSizes.push_back(chunkSize);
+
+    d->chunkOffsets.push_back(tell());
+
+    seek(chunkSize, Current);
+
+    // check padding
+    char paddingSize = 0;
+    long uPosNotPadded = tell();
+    if((uPosNotPadded & 0x01) != 0) {
+      ByteVector iByte = readBlock(1);
+      if((iByte.size() != 1) || (iByte[0] != 0)) {
+        // not well formed, re-seek
+        seek(uPosNotPadded, Beginning);
+      }
+      else {
+        paddingSize = 1;
+      }
+    }
+    d->chunkPadding.push_back(paddingSize);
+
+  }
+}
+
+void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
+                            ulong offset, ulong replace)
+{
+  ByteVector combined = name;
+  combined.append(ByteVector::fromUInt(data.size(), d->endianness == BigEndian));
+  combined.append(data);
+  if((data.size() & 0x01) != 0) {
+    // padding
+    combined.append('\x00');
+  }
+  insert(combined, offset, replace);
+}
diff --git a/src/taglib/riff/rifffile.h b/src/taglib/riff/rifffile.h
new file mode 100644 (file)
index 0000000..3b375e2
--- /dev/null
@@ -0,0 +1,105 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_RIFFFILE_H
+#define TAGLIB_RIFFFILE_H
+
+#include "taglib_export.h"
+#include "tfile.h"
+
+namespace TagLib {
+
+  //! An implementation of TagLib::File with RIFF specific methods
+
+  namespace RIFF {
+
+    //! An RIFF file class with some useful methods specific to RIFF
+
+    /*!
+     * This implements the generic TagLib::File API and additionally provides
+     * access to properties that are distinct to RIFF files, notably access
+     * to the different ID3 tags.
+     */
+
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+    protected:
+
+      enum Endianness { BigEndian, LittleEndian };
+
+      File(FileName file, Endianness endianness);
+
+      /*!
+       * \return The number of chunks in the file.
+       */
+      uint chunkCount() const;
+
+      /*!
+       * \return The offset within the file for the selected chunk number.
+       */
+      uint chunkOffset(uint i) const;
+
+      /*!
+       * \return The name of the specified chunk, for instance, "COMM" or "ID3 "
+       */
+      ByteVector chunkName(uint i) const;
+
+      /*!
+       * Reads the chunk data from the file and returns it.
+       *
+       * \note This \e will move the read pointer for the file.
+       */
+      ByteVector chunkData(uint i);
+
+      /*!
+       * Sets the data for the chunk \a name to \a data.  If a chunk with the
+       * given name already exists it will be overwritten, otherwise it will be
+       * created after the existing chunks.
+       *
+       * \warning This will update the file immediately.
+       */
+      void setChunkData(const ByteVector &name, const ByteVector &data);
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read();
+      void writeChunk(const ByteVector &name, const ByteVector &data,
+                      ulong offset, ulong replace = 0);
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/riff/wav/wavfile.cpp b/src/taglib/riff/wav/wavfile.cpp
new file mode 100644 (file)
index 0000000..b2123ad
--- /dev/null
@@ -0,0 +1,108 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tdebug.h>
+#include <id3v2tag.h>
+
+#include "wavfile.h"
+
+using namespace TagLib;
+
+class RIFF::WAV::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    properties(0),
+    tag(0)
+  {
+
+  }
+
+  ~FilePrivate()
+  {
+    delete properties;
+    delete tag;
+  }
+
+  Properties *properties;
+  ID3v2::Tag *tag;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+RIFF::WAV::File::File(FileName file, bool readProperties,
+                       Properties::ReadStyle propertiesStyle) : RIFF::File(file, LittleEndian)
+{
+  d = new FilePrivate;
+  if(isOpen())
+    read(readProperties, propertiesStyle);
+}
+
+RIFF::WAV::File::~File()
+{
+  delete d;
+}
+
+ID3v2::Tag *RIFF::WAV::File::tag() const
+{
+  return d->tag;
+}
+
+RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool RIFF::WAV::File::save()
+{
+  if(readOnly()) {
+    debug("RIFF::WAV::File::save() -- File is read only.");
+    return false;
+  }
+
+  setChunkData("ID3 ", d->tag->render());
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  for(uint i = 0; i < chunkCount(); i++) {
+    if(chunkName(i) == "ID3 ")
+      d->tag = new ID3v2::Tag(this, chunkOffset(i));
+    else if(chunkName(i) == "fmt " && readProperties)
+      d->properties = new Properties(chunkData(i), propertiesStyle);
+  }
+
+  if(!d->tag)
+    d->tag = new ID3v2::Tag;
+}
diff --git a/src/taglib/riff/wav/wavfile.h b/src/taglib/riff/wav/wavfile.h
new file mode 100644 (file)
index 0000000..6c36a3c
--- /dev/null
@@ -0,0 +1,102 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_WAVFILE_H
+#define TAGLIB_WAVFILE_H
+
+#include "rifffile.h"
+#include "id3v2tag.h"
+#include "wavproperties.h"
+
+namespace TagLib {
+
+  namespace RIFF {
+
+    //! An implementation of WAV metadata
+
+    /*!
+     * This is implementation of WAV metadata.
+     *
+     * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
+     * chunk as well as properties from the file.
+     */
+
+    namespace WAV {
+
+      //! An implementation of TagLib::File with WAV specific methods
+
+      /*!
+       * This implements and provides an interface for WAV files to the
+       * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+       * the abstract TagLib::File API as well as providing some additional
+       * information specific to WAV files.
+       */
+
+      class TAGLIB_EXPORT File : public TagLib::RIFF::File
+      {
+      public:
+        /*!
+         * Contructs an WAV file from \a file.  If \a readProperties is true the
+         * file's audio properties will also be read using \a propertiesStyle.  If
+         * false, \a propertiesStyle is ignored.
+         */
+        File(FileName file, bool readProperties = true,
+             Properties::ReadStyle propertiesStyle = Properties::Average);
+
+        /*!
+         * Destroys this instance of the File.
+         */
+        virtual ~File();
+
+        /*!
+         * Returns the Tag for this file.
+         */
+        virtual ID3v2::Tag *tag() const;
+
+        /*!
+         * Returns the WAV::Properties for this file.  If no audio properties
+         * were read then this will return a null pointer.
+         */
+        virtual Properties *audioProperties() const;
+
+        /*!
+         * Saves the file.
+         */
+        virtual bool save();
+
+      private:
+        File(const File &);
+        File &operator=(const File &);
+
+        void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+        class FilePrivate;
+        FilePrivate *d;
+      };
+    }
+  }
+}
+
+#endif
diff --git a/src/taglib/riff/wav/wavproperties.cpp b/src/taglib/riff/wav/wavproperties.cpp
new file mode 100644 (file)
index 0000000..18920dd
--- /dev/null
@@ -0,0 +1,104 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "wavproperties.h"
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <cmath>
+#include <math.h>
+
+using namespace TagLib;
+
+class RIFF::WAV::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate() :
+    format(0),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0)
+  {
+
+  }
+
+  short format;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+RIFF::WAV::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate;
+  read(data);
+}
+
+RIFF::WAV::Properties::~Properties()
+{
+  delete d;
+}
+
+int RIFF::WAV::Properties::length() const
+{
+  return d->length;
+}
+
+int RIFF::WAV::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int RIFF::WAV::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int RIFF::WAV::Properties::channels() const
+{
+  return d->channels;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void RIFF::WAV::Properties::read(const ByteVector &data)
+{
+  d->format     = data.mid(0, 2).toShort(false);
+  d->channels   = data.mid(2, 2).toShort(false);
+  d->sampleRate = data.mid(4, 4).toUInt(false);
+  d->bitrate    = data.mid(8, 4).toUInt(false) * 8 / 1024;
+
+  // short bitsPerSample = data.mid(10, 2).toShort();
+  // d->bitrate    = (sampleRate * sampleSize * d->channels) / 1024.0;
+  // d->length     = sampleFrames / d->sampleRate;
+}
diff --git a/src/taglib/riff/wav/wavproperties.h b/src/taglib/riff/wav/wavproperties.h
new file mode 100644 (file)
index 0000000..531f877
--- /dev/null
@@ -0,0 +1,82 @@
+/***************************************************************************
+    copyright            : (C) 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_WAVPROPERTIES_H
+#define TAGLIB_WAVPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  class ByteVector;
+
+  namespace RIFF {
+
+    namespace WAV {
+
+      class File;
+
+      //! An implementation of audio property reading for WAV
+
+      /*!
+       * This reads the data from an WAV stream found in the AudioProperties
+       * API.
+       */
+
+      class TAGLIB_EXPORT Properties : public AudioProperties
+      {
+      public:
+       /*!
+        * Create an instance of WAV::Properties with the data read from the
+        * ByteVector \a data.
+        */
+       Properties(const ByteVector &data, ReadStyle style);
+
+       /*!
+        * Destroys this WAV::Properties instance.
+        */
+       virtual ~Properties();
+
+       // Reimplementations.
+
+       virtual int length() const;
+       virtual int bitrate() const;
+       virtual int sampleRate() const;
+       virtual int channels() const;
+
+      private:
+       Properties(const Properties &);
+       Properties &operator=(const Properties &);
+
+       void read(const ByteVector &data);
+
+       class PropertiesPrivate;
+       PropertiesPrivate *d;
+      };
+    }
+  }
+}
+
+#endif
diff --git a/src/taglib/tag.cpp b/src/taglib/tag.cpp
new file mode 100644 (file)
index 0000000..2917540
--- /dev/null
@@ -0,0 +1,83 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "tag.h"
+
+using namespace TagLib;
+
+class Tag::TagPrivate
+{
+
+};
+
+Tag::Tag()
+{
+
+}
+
+Tag::~Tag()
+{
+
+}
+
+bool Tag::isEmpty() const
+{
+  return (title().isEmpty() &&
+          artist().isEmpty() &&
+          album().isEmpty() &&
+          comment().isEmpty() &&
+          genre().isEmpty() &&
+          year() == 0 &&
+          track() == 0);
+}
+
+void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static
+{
+  if(overwrite) {
+    target->setTitle(source->title());
+    target->setArtist(source->artist());
+    target->setAlbum(source->album());
+    target->setComment(source->comment());
+    target->setGenre(source->genre());
+    target->setYear(source->year());
+    target->setTrack(source->track());
+  }
+  else {
+    if(target->title().isEmpty())
+      target->setTitle(source->title());
+    if(target->artist().isEmpty())
+      target->setArtist(source->artist());
+    if(target->album().isEmpty())
+      target->setAlbum(source->album());
+    if(target->comment().isEmpty())
+      target->setComment(source->comment());
+    if(target->genre().isEmpty())
+      target->setGenre(source->genre());
+    if(target->year() <= 0)
+      target->setYear(source->year());
+    if(target->track() <= 0)
+      target->setTrack(source->track());
+  }
+}
diff --git a/src/taglib/tag.h b/src/taglib/tag.h
new file mode 100644 (file)
index 0000000..6404a70
--- /dev/null
@@ -0,0 +1,173 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_TAG_H
+#define TAGLIB_TAG_H
+
+#include "taglib_export.h"
+#include "tstring.h"
+
+namespace TagLib {
+
+  //! A simple, generic interface to common audio meta data fields
+
+  /*!
+   * This is an attempt to abstract away the difference in the meta data formats
+   * of various audio codecs and tagging schemes.  As such it is generally a
+   * subset of what is available in the specific formats but should be suitable
+   * for most applications.  This is meant to compliment the generic APIs found
+   * in TagLib::AudioProperties, TagLib::File and TagLib::FileRef.
+   */
+
+  class TAGLIB_EXPORT Tag
+  {
+  public:
+
+    /*!
+     * Detroys this Tag instance.
+     */
+    virtual ~Tag();
+
+    /*!
+     * Returns the track name; if no track name is present in the tag
+     * String::null will be returned.
+     */
+    virtual String title() const = 0;
+
+    /*!
+     * Returns the artist name; if no artist name is present in the tag
+     * String::null will be returned.
+     */
+    virtual String artist() const = 0;
+
+    /*!
+     * Returns the album name; if no album name is present in the tag
+     * String::null will be returned.
+     */
+    virtual String album() const = 0;
+
+    /*!
+     * Returns the track comment; if no comment is present in the tag
+     * String::null will be returned.
+     */
+    virtual String comment() const = 0;
+
+    /*!
+     * Returns the genre name; if no genre is present in the tag String::null
+     * will be returned.
+     */
+    virtual String genre() const = 0;
+
+    /*!
+     * Returns the year; if there is no year set, this will return 0.
+     */
+    virtual uint year() const = 0;
+
+    /*!
+     * Returns the track number; if there is no track number set, this will
+     * return 0.
+     */
+    virtual uint track() const = 0;
+
+    /*!
+     * Sets the title to \a s.  If \a s is String::null then this value will be
+     * cleared.
+     */
+    virtual void setTitle(const String &s) = 0;
+
+    /*!
+     * Sets the artist to \a s.  If \a s is String::null then this value will be
+     * cleared.
+     */
+    virtual void setArtist(const String &s) = 0;
+
+    /*!
+     * Sets the album to \a s.  If \a s is String::null then this value will be
+     * cleared.
+     */
+    virtual void setAlbum(const String &s) = 0;
+
+    /*!
+     * Sets the comment to \a s.  If \a s is String::null then this value will be
+     * cleared.
+     */
+    virtual void setComment(const String &s) = 0;
+
+    /*!
+     * Sets the genre to \a s.  If \a s is String::null then this value will be
+     * cleared.  For tag formats that use a fixed set of genres, the appropriate
+     * value will be selected based on a string comparison.  A list of available
+     * genres for those formats should be available in that type's
+     * implementation.
+     */
+    virtual void setGenre(const String &s) = 0;
+
+    /*!
+     * Sets the year to \a i.  If \a s is 0 then this value will be cleared.
+     */
+    virtual void setYear(uint i) = 0;
+
+    /*!
+     * Sets the track to \a i.  If \a s is 0 then this value will be cleared.
+     */
+    virtual void setTrack(uint i) = 0;
+
+    /*!
+     * Returns true if the tag does not contain any data.  This should be
+     * reimplemented in subclasses that provide more than the basic tagging
+     * abilities in this class.
+     */
+    virtual bool isEmpty() const;
+
+    /*!
+     * Copies the generic data from one tag to another.
+     *
+     * \note This will no affect any of the lower level details of the tag.  For
+     * instance if any of the tag type specific data (maybe a URL for a band) is
+     * set, this will not modify or copy that.  This just copies using the API
+     * in this class.
+     *
+     * If \a overwrite is true then the values will be unconditionally copied.
+     * If false only empty values will be overwritten.
+     */
+    static void duplicate(const Tag *source, Tag *target, bool overwrite = true);
+
+  protected:
+    /*!
+     * Construct a Tag.  This is protected since tags should only be instantiated
+     * through subclasses.
+     */
+    Tag();
+
+  private:
+    Tag(const Tag &);
+    Tag &operator=(const Tag &);
+
+    class TagPrivate;
+    TagPrivate *d;
+  };
+}
+
+#endif
diff --git a/src/taglib/taglib_config.h.in b/src/taglib/taglib_config.h.in
new file mode 100644 (file)
index 0000000..7be1170
--- /dev/null
@@ -0,0 +1,5 @@
+/* With ASF support */
+#undef TAGLIB_WITH_ASF
+
+/* With MP4 support */
+#undef TAGLIB_WITH_MP4
diff --git a/src/taglib/taglib_export.h b/src/taglib/taglib_export.h
new file mode 100644 (file)
index 0000000..838d746
--- /dev/null
@@ -0,0 +1,47 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_EXPORT_H
+#define TAGLIB_EXPORT_H
+
+#if !defined(TAGLIB_STATIC) && (defined(_WIN32) || defined(_WIN64))
+#ifdef MAKE_TAGLIB_LIB
+#define TAGLIB_EXPORT __declspec(dllexport)
+#else
+#define TAGLIB_EXPORT __declspec(dllimport)
+#endif
+#elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 1)
+#define TAGLIB_EXPORT __attribute__ ((visibility("default")))
+#else
+#define TAGLIB_EXPORT
+#endif
+
+#define TAGLIB_NO_CONFIG
+
+#ifndef TAGLIB_NO_CONFIG
+#include "taglib_config.h"
+#endif
+
+#endif
diff --git a/src/taglib/tagunion.cpp b/src/taglib/tagunion.cpp
new file mode 100644 (file)
index 0000000..08cc140
--- /dev/null
@@ -0,0 +1,185 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "tagunion.h"
+
+using namespace TagLib;
+
+#define stringUnion(method)                                          \
+  if(tag(0) && !tag(0)->method().isEmpty())                          \
+    return tag(0)->method();                                         \
+  if(tag(1) && !tag(1)->method().isEmpty())                          \
+    return tag(1)->method();                                         \
+  if(tag(2) && !tag(2)->method().isEmpty())                          \
+    return tag(2)->method();                                         \
+  return String::null                                                \
+
+#define numberUnion(method)                                          \
+  if(tag(0) && tag(0)->method() > 0)                                 \
+    return tag(0)->method();                                         \
+  if(tag(1) && tag(1)->method() > 0)                                 \
+    return tag(1)->method();                                         \
+  if(tag(2) && tag(2)->method() > 0)                                 \
+    return tag(2)->method();                                         \
+  return 0
+
+#define setUnion(method, value)                                      \
+  if(tag(0))                                                         \
+    tag(0)->set##method(value);                                      \
+  if(tag(1))                                                         \
+    tag(1)->set##method(value);                                      \
+  if(tag(2))                                                         \
+    tag(2)->set##method(value);                                      \
+
+class TagUnion::TagUnionPrivate
+{
+public:
+  TagUnionPrivate() : tags(3, static_cast<Tag *>(0))
+  {
+
+  }
+
+  ~TagUnionPrivate()
+  {
+    delete tags[0];
+    delete tags[1];
+    delete tags[2];
+  }
+
+  std::vector<Tag *> tags;
+};
+
+TagUnion::TagUnion(Tag *first, Tag *second, Tag *third)
+{
+  d = new TagUnionPrivate;
+
+  d->tags[0] = first;
+  d->tags[1] = second;
+  d->tags[2] = third;
+}
+
+TagUnion::~TagUnion()
+{
+  delete d;
+}
+
+Tag *TagUnion::operator[](int index) const
+{
+  return tag(index);
+}
+
+Tag *TagUnion::tag(int index) const
+{
+  return d->tags[index];
+}
+
+void TagUnion::set(int index, Tag *tag)
+{
+  delete d->tags[index];
+  d->tags[index] = tag;
+}
+
+String TagUnion::title() const
+{
+  stringUnion(title);
+}
+
+String TagUnion::artist() const
+{
+  stringUnion(artist);
+}
+
+String TagUnion::album() const
+{
+  stringUnion(album);
+}
+
+String TagUnion::comment() const
+{
+  stringUnion(comment);
+}
+
+String TagUnion::genre() const
+{
+  stringUnion(genre);
+}
+
+TagLib::uint TagUnion::year() const
+{
+  numberUnion(year);
+}
+
+TagLib::uint TagUnion::track() const
+{
+  numberUnion(track);
+}
+
+void TagUnion::setTitle(const String &s)
+{
+  setUnion(Title, s);
+}
+
+void TagUnion::setArtist(const String &s)
+{
+  setUnion(Artist, s);
+}
+
+void TagUnion::setAlbum(const String &s)
+{
+  setUnion(Album, s);
+}
+
+void TagUnion::setComment(const String &s)
+{
+  setUnion(Comment, s);
+}
+
+void TagUnion::setGenre(const String &s)
+{
+  setUnion(Genre, s);
+}
+
+void TagUnion::setYear(uint i)
+{
+  setUnion(Year, i);
+}
+
+void TagUnion::setTrack(uint i)
+{
+  setUnion(Track, i);
+}
+
+bool TagUnion::isEmpty() const
+{
+  if(d->tags[0] && !d->tags[0]->isEmpty())
+    return false;
+  if(d->tags[1] && !d->tags[1]->isEmpty())
+    return false;
+  if(d->tags[2] && !d->tags[2]->isEmpty())
+    return false;
+
+  return true;
+}
+
diff --git a/src/taglib/tagunion.h b/src/taglib/tagunion.h
new file mode 100644 (file)
index 0000000..76d407c
--- /dev/null
@@ -0,0 +1,95 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_TAGUNION_H
+#define TAGLIB_TAGUNION_H
+
+#include "tag.h"
+
+#ifndef DO_NOT_DOCUMENT
+
+namespace TagLib {
+
+  /*!
+   * \internal
+   */
+
+  class TagUnion : public Tag
+  {
+  public:
+
+    enum AccessType { Read, Write };
+
+    /*!
+     * Creates a TagLib::Tag that is the union of \a first, \a second, and
+     * \a third.  The TagUnion takes ownership of these tags and will handle
+     * their deletion.
+     */
+    TagUnion(Tag *first = 0, Tag *second = 0, Tag *third = 0);
+
+    virtual ~TagUnion();
+
+    Tag *operator[](int index) const;
+    Tag *tag(int index) const;
+
+    void set(int index, Tag *tag);
+
+    virtual String title() const;
+    virtual String artist() const;
+    virtual String album() const;
+    virtual String comment() const;
+    virtual String genre() const;
+    virtual uint year() const;
+    virtual uint track() const;
+
+    virtual void setTitle(const String &s);
+    virtual void setArtist(const String &s);
+    virtual void setAlbum(const String &s);
+    virtual void setComment(const String &s);
+    virtual void setGenre(const String &s);
+    virtual void setYear(uint i);
+    virtual void setTrack(uint i);
+    virtual bool isEmpty() const;
+
+    template <class T> T *access(int index, bool create)
+    {
+      if(!create || tag(index))
+        return static_cast<T *>(tag(index));
+
+      set(index, new T);
+      return static_cast<T *>(tag(index));
+    }
+
+  private:
+    TagUnion(const Tag &);
+    TagUnion &operator=(const Tag &);
+
+    class TagUnionPrivate;
+    TagUnionPrivate *d;
+  };
+}
+
+#endif
+#endif
diff --git a/src/taglib/toolkit/taglib.h b/src/taglib/toolkit/taglib.h
new file mode 100644 (file)
index 0000000..0050e06
--- /dev/null
@@ -0,0 +1,183 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_H
+#define TAGLIB_H
+
+#define TAGLIB_MAJOR_VERSION 1
+#define TAGLIB_MINOR_VERSION 6
+#define TAGLIB_PATCH_VERSION 3
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1))
+#define TAGLIB_IGNORE_MISSING_DESTRUCTOR _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"")
+#else
+#define TAGLIB_IGNORE_MISSING_DESTRUCTOR
+#endif
+
+#include <string>
+
+//! A namespace for all TagLib related classes and functions
+
+/*!
+ * This namespace contains everything in TagLib.  For projects working with
+ * TagLib extensively it may be conveniten to add a
+ * \code
+ * using namespace TagLib;
+ * \endcode
+ */
+
+namespace TagLib {
+
+  class String;
+
+  typedef wchar_t wchar;
+  typedef unsigned char uchar;
+  typedef unsigned int  uint;
+  typedef unsigned long ulong;
+
+  /*!
+   * Unfortunately std::wstring isn't defined on some systems, (i.e. GCC < 3)
+   * so I'm providing something here that should be constant.
+   */
+  typedef std::basic_string<wchar> wstring;
+
+#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class.
+  /*!
+   * \internal
+   * This is just used as a base class for shared classes in TagLib.
+   *
+   * \warning This <b>is not</b> part of the TagLib public API!
+   */
+
+  class RefCounter
+  {
+  public:
+    RefCounter() : refCount(1) {}
+    void ref() { refCount++; }
+    bool deref() { return ! --refCount ; }
+    int count() { return refCount; }
+  private:
+    uint refCount;
+  };
+
+#endif // DO_NOT_DOCUMENT
+
+}
+
+/*!
+ * \mainpage TagLib
+ *
+ * \section intro Introduction
+ *
+ * TagLib is a library for reading and editing audio meta data, commonly know as \e tags.
+ *
+ * Features:
+ * - A clean, high level, C++ API to handling audio meta data.
+ * - Format specific APIs for advanced API users.
+ * - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats.
+ * - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis and Speex file formats.
+ * - Basic audio file properties such as length, sample rate, etc.
+ * - Long term binary and source compatibility.
+ * - Extensible design, notably the ability to add other formats or extend current formats as a library user.
+ * - Full support for unicode and internationalized tags.
+ * - Dual <a href="http://www.mozilla.org/MPL/MPL-1.1.html">MPL</a> and
+ *   <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">LGPL</a> licenses.
+ * - No external toolkit dependancies.
+ *
+ * \section why Why TagLib?
+ *
+ * TagLib originally was written to provide an updated and improved ID3v2 implementation in C++ for use
+ * in a variety of Open Source projects.  Since development began in 2002 and the 1.0 release in 2004
+ * it has expanded to cover a wide variety of tag and file formats and is used in a wide variety of
+ * Open Source and proprietary applications.  It now supports a variety of UNIXes, including Apple's OS
+ * X, as well as Microsoft Windows.
+ *
+ * \section commercial Usage in Commercial Applications
+ *
+ * TagLib's licenses \e do allow usage within propriety (\e closed) applications, however TagLib is \e not
+ * public domain.  Please note the requirements of the LGPL or MPL, and adhere to at least one of them.
+ * In simple terms, you must at a minimum note your usage of TagLib, note the licensing terms of TagLib and
+ * if you make changes to TagLib publish them.  Please review the licenses above before using TagLib in your
+ * software.  Note that you may choose either the MPL or the LGPL, you do not have to fulfill the
+ * requirements of both.
+ *
+ * \section installing Installing TagLib
+ *
+ * Please see the <a href="http://developer.kde.org/~wheeler/taglib.html">TagLib website</a> for the latest
+ * downloads.
+ *
+ * Instructions for installing TagLib vary per platform, but generally speaking on UNIX standard configure and
+ * make commands are provided.  TagLib installs a taglib-config and package-config file to make it easier to
+ * integrate into various build systems.  Note that TagLib's include install directory \e must be included in
+ * the header include path.  Simply adding <taglib/tag.h> will \e not work.
+ *
+ * On Windows, TagLib can be built using the CMake build systems.
+ *
+ * \section start Getting Started
+ *
+ * TagLib provides both simple, abstract APIs which make it possible to ignore the differences between tagging
+ * formats and format specific APIs which allow programmers to work with the features of specific tagging
+ * schemes.  There is a similar abstraction mechanism for AudioProperties.
+ *
+ * The best place to start is with the <b>Class Hierarchy</b> linked at the top of the page.  The File and
+ * AudioProperties classes and their subclasses are the core of TagLib.  The FileRef class is also a convenient
+ * way for using a value-based handle.
+ *
+ * \note When working with FileRef please consider that it has only the most basic (extension-based) file
+ * type resolution.  Please see its documentation on how to plug in more advanced file type resolution.  (Such
+ * resolution may be part of later TagLib releases by default.)
+ *
+ * Here's a very simple example with TagLib:
+ *
+ * \code
+ *
+ * TagLib::FileRef f("Latex Solar Beef.mp3");
+ * TagLib::String artist = f.tag()->artist(); // artist == "Frank Zappa"
+ *
+ * f.tag()->setAlbum("Fillmore East");
+ * f.save();
+ *
+ * TagLib::FileRef g("Free City Rhymes.ogg");
+ * TagLib::String album = g.tag()->album(); // album == "NYC Ghosts & Flowers"
+ *
+ * g.tag()->setTrack(1);
+ * g.save();
+ *
+ * \endcode
+ *
+ * More examples can be found in the \e examples directory of the source distribution.
+ *
+ * \section Contact
+ *
+ * Questions about TagLib should be directed to the TagLib mailing list, not directly to the author.
+ *
+ *  - <a href="http://developer.kde.org/~wheeler/taglib/">TagLib Homepage</a>
+ *  - <a href="https://mail.kde.org/mailman/listinfo/taglib-devel">TagLib Mailing List (taglib-devel@kde.org)</a>
+ *
+ * \author Scott Wheeler <wheeler@kde.org> et al.
+ *
+ */
+
+#endif
diff --git a/src/taglib/toolkit/tbytevector.cpp b/src/taglib/toolkit/tbytevector.cpp
new file mode 100644 (file)
index 0000000..7667977
--- /dev/null
@@ -0,0 +1,677 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <iostream>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include <string.h>
+
+#include "tbytevector.h"
+
+// This is a bit ugly to keep writing over and over again.
+
+// A rather obscure feature of the C++ spec that I hadn't thought of that makes
+// working with C libs much more effecient.  There's more here:
+//
+// http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp
+
+#define DATA(x) (&(x->data[0]))
+
+namespace TagLib {
+  static const uint crcTable[256] = {
+    0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+    0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+    0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+    0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+    0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+    0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+    0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+    0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+    0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+    0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+    0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+    0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+    0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+    0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+    0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+    0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+    0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+    0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+    0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+    0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+    0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+    0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+    0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+    0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+    0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+    0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+    0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+    0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+    0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+    0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+    0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+    0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+    0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+    0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+    0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+    0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+    0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+    0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+    0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+    0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+    0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+    0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+    0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+  };
+
+  /*!
+   * A templatized KMP find that works both with a ByteVector and a ByteVectorMirror.
+   */
+
+  template <class Vector>
+  int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign)
+  {
+    if(pattern.size() > v.size() || offset > v.size() - 1)
+      return -1;
+
+    // Let's go ahead and special case a pattern of size one since that's common
+    // and easy to make fast.
+
+    if(pattern.size() == 1) {
+      char p = pattern[0];
+      for(uint i = offset; i < v.size(); i++) {
+        if(v[i] == p && (i - offset) % byteAlign == 0)
+          return i;
+      }
+      return -1;
+    }
+
+    uchar lastOccurrence[256];
+
+    for(uint i = 0; i < 256; ++i)
+      lastOccurrence[i] = uchar(pattern.size());
+
+    for(uint i = 0; i < pattern.size() - 1; ++i)
+      lastOccurrence[uchar(pattern[i])] = uchar(pattern.size() - i - 1);
+
+    for(uint i = pattern.size() - 1 + offset; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) {
+      int iBuffer = i;
+      int iPattern = pattern.size() - 1;
+
+      while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) {
+        --iBuffer;
+        --iPattern;
+      }
+
+      if(-1 == iPattern && (iBuffer + 1 - offset) % byteAlign == 0)
+        return iBuffer + 1;
+    }
+
+    return -1;
+  }
+
+  /*!
+   * Wraps the accessors to a ByteVector to make the search algorithm access the
+   * elements in reverse.
+   *
+   * \see vectorFind()
+   * \see ByteVector::rfind()
+   */
+
+  class ByteVectorMirror
+  {
+  public:
+    ByteVectorMirror(const ByteVector &source) : v(source) {}
+
+    char operator[](int index) const
+    {
+      return v[v.size() - index - 1];
+    }
+
+    char at(int index) const
+    {
+      return v.at(v.size() - index - 1);
+    }
+
+    ByteVectorMirror mid(uint index, uint length = 0xffffffff) const
+    {
+      return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length);
+    }
+
+    uint size() const
+    {
+      return v.size();
+    }
+
+    int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const
+    {
+      ByteVectorMirror v(*this);
+
+      if(offset > 0) {
+        offset = size() - offset - pattern.size();
+        if(offset >= size())
+          offset = 0;
+      }
+
+      const int pos = vectorFind<ByteVectorMirror>(v, pattern, offset, byteAlign);
+
+      // If the offset is zero then we need to adjust the location in the search
+      // to be appropriately reversed.  If not we need to account for the fact
+      // that the recursive call (called from the above line) has already ajusted
+      // for this but that the normal templatized find above will add the offset
+      // to the returned value.
+      //
+      // This is a little confusing at first if you don't first stop to think
+      // through the logic involved in the forward search.
+
+      if(pos == -1)
+        return -1;
+
+      return size() - pos - pattern.size();
+    }
+
+  private:
+    const ByteVector &v;
+  };
+
+  template <class T>
+  T toNumber(const std::vector<char> &data, bool mostSignificantByteFirst)
+  {
+    T sum = 0;
+
+    if(data.size() <= 0) {
+      debug("ByteVectorMirror::toNumber<T>() -- data is empty, returning 0");
+      return sum;
+    }
+
+    uint size = sizeof(T);
+    uint last = data.size() > size ? size - 1 : data.size() - 1;
+
+    for(uint i = 0; i <= last; i++)
+      sum |= (T) uchar(data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
+
+    return sum;
+  }
+
+  template <class T>
+  ByteVector fromNumber(T value, bool mostSignificantByteFirst)
+  {
+    int size = sizeof(T);
+
+    ByteVector v(size, 0);
+
+    for(int i = 0; i < size; i++)
+      v[i] = uchar(value >> ((mostSignificantByteFirst ? size - 1 - i : i) * 8) & 0xff);
+
+    return v;
+  }
+}
+
+using namespace TagLib;
+
+class ByteVector::ByteVectorPrivate : public RefCounter
+{
+public:
+  ByteVectorPrivate() : RefCounter(), size(0) {}
+  ByteVectorPrivate(const std::vector<char> &v) : RefCounter(), data(v), size(v.size()) {}
+  ByteVectorPrivate(TagLib::uint len, char value) : RefCounter(), data(len, value), size(len) {}
+
+  std::vector<char> data;
+
+  // std::vector<T>::size() is very slow, so we'll cache the value
+
+  uint size;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+ByteVector ByteVector::null;
+
+ByteVector ByteVector::fromCString(const char *s, uint length)
+{
+  ByteVector v;
+
+  if(length == 0xffffffff)
+    v.setData(s);
+  else
+    v.setData(s, length);
+
+  return v;
+}
+
+ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
+{
+  return fromNumber<uint>(value, mostSignificantByteFirst);
+}
+
+ByteVector ByteVector::fromShort(short value, bool mostSignificantByteFirst)
+{
+  return fromNumber<short>(value, mostSignificantByteFirst);
+}
+
+ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst)
+{
+  return fromNumber<long long>(value, mostSignificantByteFirst);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ByteVector::ByteVector()
+{
+  d = new ByteVectorPrivate;
+}
+
+ByteVector::ByteVector(uint size, char value)
+{
+  d = new ByteVectorPrivate(size, value);
+}
+
+ByteVector::ByteVector(const ByteVector &v) : d(v.d)
+{
+  d->ref();
+}
+
+ByteVector::ByteVector(char c)
+{
+  d = new ByteVectorPrivate;
+  d->data.push_back(c);
+  d->size = 1;
+}
+
+ByteVector::ByteVector(const char *data, uint length)
+{
+  d = new ByteVectorPrivate;
+  setData(data, length);
+}
+
+ByteVector::ByteVector(const char *data)
+{
+  d = new ByteVectorPrivate;
+  setData(data);
+}
+
+ByteVector::~ByteVector()
+{
+  if(d->deref())
+    delete d;
+}
+
+ByteVector &ByteVector::setData(const char *data, uint length)
+{
+  detach();
+
+  resize(length);
+
+  if(length > 0)
+    ::memcpy(DATA(d), data, length);
+
+  return *this;
+}
+
+ByteVector &ByteVector::setData(const char *data)
+{
+  return setData(data, ::strlen(data));
+}
+
+char *ByteVector::data()
+{
+  detach();
+  return size() > 0 ? DATA(d) : 0;
+}
+
+const char *ByteVector::data() const
+{
+  return size() > 0 ? DATA(d) : 0;
+}
+
+ByteVector ByteVector::mid(uint index, uint length) const
+{
+  ByteVector v;
+
+  if(index > size())
+    return v;
+
+  ConstIterator endIt;
+
+  if(length < 0xffffffff && length + index < size())
+    endIt = d->data.begin() + index + length;
+  else
+    endIt = d->data.end();
+
+  v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt);
+  v.d->size = v.d->data.size();
+
+  return v;
+}
+
+char ByteVector::at(uint index) const
+{
+  return index < size() ? d->data[index] : 0;
+}
+
+int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const
+{
+  return vectorFind<ByteVector>(*this, pattern, offset, byteAlign);
+}
+
+int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const
+{
+  // Ok, this is a little goofy, but pretty cool after it sinks in.  Instead of
+  // reversing the find method's Boyer-Moore search algorithm I created a "mirror"
+  // for a ByteVector to reverse the behavior of the accessors.
+
+  ByteVectorMirror v(*this);
+  ByteVectorMirror p(pattern);
+
+  return v.find(p, offset, byteAlign);
+}
+
+bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const
+{
+  if(pattern.size() < patternLength)
+    patternLength = pattern.size();
+
+  // do some sanity checking -- all of these things are needed for the search to be valid
+
+  if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0)
+    return false;
+
+  // loop through looking for a mismatch
+
+  for(uint i = 0; i < patternLength - patternOffset; i++) {
+    if(at(i + offset) != pattern[i + patternOffset])
+      return false;
+  }
+
+  return true;
+}
+
+bool ByteVector::startsWith(const ByteVector &pattern) const
+{
+  return containsAt(pattern, 0);
+}
+
+bool ByteVector::endsWith(const ByteVector &pattern) const
+{
+  return containsAt(pattern, size() - pattern.size());
+}
+
+ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with)
+{
+  if(pattern.size() == 0 || pattern.size() > size())
+    return *this;
+
+  const int patternSize = pattern.size();
+  const int withSize = with.size();
+
+  int offset = find(pattern);
+
+  while(offset >= 0) {
+
+    const int originalSize = size();
+
+    if(withSize > patternSize)
+      resize(originalSize + withSize - patternSize);
+
+    if(patternSize != withSize)
+      ::memcpy(data() + offset + withSize, mid(offset + patternSize).data(), originalSize - offset - patternSize);
+
+    if(withSize < patternSize)
+      resize(originalSize + withSize - patternSize);
+
+    ::memcpy(data() + offset, with.data(), withSize);
+
+    offset = find(pattern, offset + withSize);
+  }
+
+  return *this;
+}
+
+int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const
+{
+  if(pattern.size() > size())
+    return -1;
+
+  const int startIndex = size() - pattern.size();
+
+  // try to match the last n-1 bytes from the vector (where n is the pattern
+  // size) -- continue trying to match n-2, n-3...1 bytes
+
+  for(uint i = 1; i < pattern.size(); i++) {
+    if(containsAt(pattern, startIndex + i, 0, pattern.size() - i))
+      return startIndex + i;
+  }
+
+  return -1;
+}
+
+ByteVector &ByteVector::append(const ByteVector &v)
+{
+  if(v.d->size == 0)
+    return *this; // Simply return if appending nothing.
+
+  detach();
+
+  uint originalSize = d->size;
+  resize(d->size + v.d->size);
+  ::memcpy(DATA(d) + originalSize, DATA(v.d), v.size());
+
+  return *this;
+}
+
+ByteVector &ByteVector::clear()
+{
+  detach();
+  d->data.clear();
+  d->size = 0;
+
+  return *this;
+}
+
+TagLib::uint ByteVector::size() const
+{
+  return d->size;
+}
+
+ByteVector &ByteVector::resize(uint size, char padding)
+{
+  if(d->size < size) {
+    d->data.reserve(size);
+    d->data.insert(d->data.end(), size - d->size, padding);
+  }
+  else
+    d->data.erase(d->data.begin() + size, d->data.end());
+
+  d->size = size;
+
+  return *this;
+}
+
+ByteVector::Iterator ByteVector::begin()
+{
+  return d->data.begin();
+}
+
+ByteVector::ConstIterator ByteVector::begin() const
+{
+  return d->data.begin();
+}
+
+ByteVector::Iterator ByteVector::end()
+{
+  return d->data.end();
+}
+
+ByteVector::ConstIterator ByteVector::end() const
+{
+  return d->data.end();
+}
+
+bool ByteVector::isNull() const
+{
+  return d == null.d;
+}
+
+bool ByteVector::isEmpty() const
+{
+  return d->data.size() == 0;
+}
+
+TagLib::uint ByteVector::checksum() const
+{
+  uint sum = 0;
+  for(ByteVector::ConstIterator it = begin(); it != end(); ++it)
+    sum = (sum << 8) ^ crcTable[((sum >> 24) & 0xff) ^ uchar(*it)];
+  return sum;
+}
+
+TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const
+{
+  return toNumber<uint>(d->data, mostSignificantByteFirst);
+}
+
+short ByteVector::toShort(bool mostSignificantByteFirst) const
+{
+  return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
+}
+
+long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
+{
+  return toNumber<unsigned long long>(d->data, mostSignificantByteFirst);
+}
+
+const char &ByteVector::operator[](int index) const
+{
+  return d->data[index];
+}
+
+char &ByteVector::operator[](int index)
+{
+  detach();
+
+  return d->data[index];
+}
+
+bool ByteVector::operator==(const ByteVector &v) const
+{
+  if(d->size != v.d->size)
+    return false;
+
+  return ::memcmp(data(), v.data(), size()) == 0;
+}
+
+bool ByteVector::operator!=(const ByteVector &v) const
+{
+  return !operator==(v);
+}
+
+bool ByteVector::operator==(const char *s) const
+{
+  if(d->size != ::strlen(s))
+    return false;
+
+  return ::memcmp(data(), s, d->size) == 0;
+}
+
+bool ByteVector::operator!=(const char *s) const
+{
+  return !operator==(s);
+}
+
+bool ByteVector::operator<(const ByteVector &v) const
+{
+  int result = ::memcmp(data(), v.data(), d->size < v.d->size ? d->size : v.d->size);
+
+  if(result != 0)
+    return result < 0;
+  else
+    return size() < v.size();
+}
+
+bool ByteVector::operator>(const ByteVector &v) const
+{
+  return v < *this;
+}
+
+ByteVector ByteVector::operator+(const ByteVector &v) const
+{
+  ByteVector sum(*this);
+  sum.append(v);
+  return sum;
+}
+
+ByteVector &ByteVector::operator=(const ByteVector &v)
+{
+  if(&v == this)
+    return *this;
+
+  if(d->deref())
+    delete d;
+
+  d = v.d;
+  d->ref();
+  return *this;
+}
+
+ByteVector &ByteVector::operator=(char c)
+{
+  *this = ByteVector(c);
+  return *this;
+}
+
+ByteVector &ByteVector::operator=(const char *data)
+{
+  *this = ByteVector(data);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void ByteVector::detach()
+{
+  if(d->count() > 1) {
+    d->deref();
+    d = new ByteVectorPrivate(d->data);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+std::ostream &operator<<(std::ostream &s, const ByteVector &v)
+{
+  for(TagLib::uint i = 0; i < v.size(); i++)
+    s << v[i];
+  return s;
+}
diff --git a/src/taglib/toolkit/tbytevector.h b/src/taglib/toolkit/tbytevector.h
new file mode 100644 (file)
index 0000000..16d42e6
--- /dev/null
@@ -0,0 +1,409 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_BYTEVECTOR_H
+#define TAGLIB_BYTEVECTOR_H
+
+#include "taglib.h"
+#include "taglib_export.h"
+
+#include <vector>
+#include <iostream>
+
+namespace TagLib {
+
+  //! A byte vector
+
+  /*!
+   * This class provides a byte vector with some methods that are useful for
+   * tagging purposes.  Many of the search functions are tailored to what is
+   * useful for finding tag related paterns in a data array.
+   */
+
+  class TAGLIB_EXPORT ByteVector
+  {
+  public:
+#ifndef DO_NOT_DOCUMENT
+    typedef std::vector<char>::iterator Iterator;
+    typedef std::vector<char>::const_iterator ConstIterator;
+#endif
+
+    /*!
+     * Constructs an empty byte vector.
+     */
+    ByteVector();
+
+    /*!
+     * Construct a vector of size \a size with all values set to \a value by
+     * default.
+     */
+    ByteVector(uint size, char value = 0);
+
+    /*!
+     * Contructs a byte vector that is a copy of \a v.
+     */
+    ByteVector(const ByteVector &v);
+
+    /*!
+     * Contructs a byte vector that contains \a c.
+     */
+    ByteVector(char c);
+
+    /*!
+     * Constructs a byte vector that copies \a data for up to \a length bytes.
+     */
+    ByteVector(const char *data, uint length);
+
+    /*!
+     * Constructs a byte vector that copies \a data up to the first null
+     * byte.  The behavior is undefined if \a data is not null terminated.
+     * This is particularly useful for constructing byte arrays from string
+     * constants.
+     */
+    ByteVector(const char *data);
+
+    /*!
+     * Destroys this ByteVector instance.
+     */
+    virtual ~ByteVector();
+
+    /*!
+     * Sets the data for the byte array using the first \a length bytes of \a data
+     */
+    ByteVector &setData(const char *data, uint length);
+
+    /*!
+     * Sets the data for the byte array copies \a data up to the first null
+     * byte.  The behavior is undefined if \a data is not null terminated.
+     */
+    ByteVector &setData(const char *data);
+
+    /*!
+     * Returns a pointer to the internal data structure.
+     *
+     * \warning Care should be taken when modifying this data structure as it is
+     * easy to corrupt the ByteVector when doing so.  Specifically, while the
+     * data may be changed, its length may not be.
+     */
+    char *data();
+
+    /*!
+     * Returns a pointer to the internal data structure which may not be modified.
+     */
+    const char *data() const;
+
+    /*!
+     * Returns a byte vector made up of the bytes starting at \a index and
+     * for \a length bytes.  If \a length is not specified it will return the bytes
+     * from \a index to the end of the vector.
+     */
+    ByteVector mid(uint index, uint length = 0xffffffff) const;
+
+    /*!
+     * This essentially performs the same as operator[](), but instead of causing
+     * a runtime error if the index is out of bounds, it will return a null byte.
+     */
+    char at(uint index) const;
+
+    /*!
+     * Searches the ByteVector for \a pattern starting at \a offset and returns
+     * the offset.  Returns -1 if the pattern was not found.  If \a byteAlign is
+     * specified the pattern will only be matched if it starts on a byte divisible
+     * by \a byteAlign (starting from \a offset).
+     */
+    int find(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
+
+    /*!
+     * Searches the ByteVector for \a pattern starting from either the end of the
+     * vector or \a offset and returns the offset.  Returns -1 if the pattern was
+     * not found.  If \a byteAlign is specified the pattern will only be matched
+     * if it starts on a byte divisible by \a byteAlign (starting from \a offset).
+     */
+    int rfind(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
+
+    /*!
+     * Checks to see if the vector contains the \a pattern starting at position
+     * \a offset.  Optionally, if you only want to search for part of the pattern
+     * you can specify an offset within the pattern to start from.  Also, you can
+     * specify to only check for the first \a patternLength bytes of \a pattern with
+     * the \a patternLength argument.
+     */
+    bool containsAt(const ByteVector &pattern, uint offset, uint patternOffset = 0, uint patternLength = 0xffffffff) const;
+
+    /*!
+     * Returns true if the vector starts with \a pattern.
+     */
+    bool startsWith(const ByteVector &pattern) const;
+
+    /*!
+     * Returns true if the vector ends with \a pattern.
+     */
+    bool endsWith(const ByteVector &pattern) const;
+
+    /*!
+     * Replaces \a pattern with \a with and returns a reference to the ByteVector
+     * after the operation.  This \e does modify the vector.
+     */
+    ByteVector &replace(const ByteVector &pattern, const ByteVector &with);
+
+    /*!
+     * Checks for a partial match of \a pattern at the end of the vector.  It
+     * returns the offset of the partial match within the vector, or -1 if the
+     * pattern is not found.  This method is particularly useful when searching for
+     * patterns that start in one vector and end in another.  When combined with
+     * startsWith() it can be used to find a pattern that overlaps two buffers.
+     *
+     * \note This will not match the complete pattern at the end of the string; use
+     * endsWith() for that.
+     */
+    int endsWithPartialMatch(const ByteVector &pattern) const;
+
+    /*!
+     * Appends \a v to the end of the ByteVector.
+     */
+    ByteVector &append(const ByteVector &v);
+
+    /*!
+     * Clears the data.
+     */
+    ByteVector &clear();
+
+    /*!
+     * Returns the size of the array.
+     */
+    uint size() const;
+
+    /*!
+     * Resize the vector to \a size.  If the vector is currently less than
+     * \a size, pad the remaining spaces with \a padding.  Returns a reference
+     * to the resized vector.
+     */
+    ByteVector &resize(uint size, char padding = 0);
+
+    /*!
+     * Returns an Iterator that points to the front of the vector.
+     */
+    Iterator begin();
+
+    /*!
+     * Returns a ConstIterator that points to the front of the vector.
+     */
+    ConstIterator begin() const;
+
+    /*!
+     * Returns an Iterator that points to the back of the vector.
+     */
+    Iterator end();
+
+    /*!
+     * Returns a ConstIterator that points to the back of the vector.
+     */
+    ConstIterator end() const;
+
+    /*!
+     * Returns true if the vector is null.
+     *
+     * \note A vector may be empty without being null.
+     * \see isEmpty()
+     */
+    bool isNull() const;
+
+    /*!
+     * Returns true if the ByteVector is empty.
+     *
+     * \see size()
+     * \see isNull()
+     */
+    bool isEmpty() const;
+
+    /*!
+     * Returns a CRC checksum of the byte vector's data.
+     */
+    uint checksum() const;
+
+    /*!
+     * Converts the first 4 bytes of the vector to an unsigned integer.
+     *
+     * If \a mostSignificantByteFirst is true this will operate left to right
+     * evaluating the integer.  For example if \a mostSignificantByteFirst is
+     * true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
+     * 0x01000000 == 1.
+     *
+     * \see fromUInt()
+     */
+    uint toUInt(bool mostSignificantByteFirst = true) const;
+
+    /*!
+     * Converts the first 2 bytes of the vector to a short.
+     *
+     * If \a mostSignificantByteFirst is true this will operate left to right
+     * evaluating the integer.  For example if \a mostSignificantByteFirst is
+     * true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1.
+     *
+     * \see fromShort()
+     */
+    short toShort(bool mostSignificantByteFirst = true) const;
+
+    /*!
+     * Converts the first 8 bytes of the vector to a (signed) long long.
+     *
+     * If \a mostSignificantByteFirst is true this will operate left to right
+     * evaluating the integer.  For example if \a mostSignificantByteFirst is
+     * true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1,
+     * if false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
+     *
+     * \see fromUInt()
+     */
+    long long toLongLong(bool mostSignificantByteFirst = true) const;
+
+    /*!
+     * Creates a 4 byte ByteVector based on \a value.  If
+     * \a mostSignificantByteFirst is true, then this will operate left to right
+     * in building the ByteVector.  For example if \a mostSignificantByteFirst is
+     * true then $00 00 00 01 == 0x00000001 == 1, if false, $01 00 00 00 ==
+     * 0x01000000 == 1.
+     *
+     * \see toUInt()
+     */
+    static ByteVector fromUInt(uint value, bool mostSignificantByteFirst = true);
+
+    /*!
+     * Creates a 2 byte ByteVector based on \a value.  If
+     * \a mostSignificantByteFirst is true, then this will operate left to right
+     * in building the ByteVector.  For example if \a mostSignificantByteFirst is
+     * true then $00 01 == 0x0001 == 1, if false, $01 00 == 0x0100 == 1.
+     *
+     * \see toShort()
+     */
+    static ByteVector fromShort(short value, bool mostSignificantByteFirst = true);
+
+    /*!
+     * Creates a 8 byte ByteVector based on \a value.  If
+     * \a mostSignificantByteFirst is true, then this will operate left to right
+     * in building the ByteVector.  For example if \a mostSignificantByteFirst is
+     * true then $00 00 00 01 == 0x0000000000000001 == 1, if false,
+     * $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
+     *
+     * \see toLongLong()
+     */
+    static ByteVector fromLongLong(long long value, bool mostSignificantByteFirst = true);
+
+    /*!
+     * Returns a ByteVector based on the CString \a s.
+     */
+    static ByteVector fromCString(const char *s, uint length = 0xffffffff);
+
+    /*!
+     * Returns a const refernence to the byte at \a index.
+     */
+    const char &operator[](int index) const;
+
+    /*!
+     * Returns a reference to the byte at \a index.
+     */
+    char &operator[](int index);
+
+    /*!
+     * Returns true if this ByteVector and \a v are equal.
+     */
+    bool operator==(const ByteVector &v) const;
+
+    /*!
+     * Returns true if this ByteVector and \a v are not equal.
+     */
+    bool operator!=(const ByteVector &v) const;
+
+    /*!
+     * Returns true if this ByteVector and the null terminated C string \a s
+     * contain the same data.
+     */
+    bool operator==(const char *s) const;
+
+    /*!
+     * Returns true if this ByteVector and the null terminated C string \a s
+     * do not contain the same data.
+     */
+    bool operator!=(const char *s) const;
+
+    /*!
+     * Returns true if this ByteVector is less than \a v.  The value of the
+     * vectors is determined by evaluating the character from left to right, and
+     * in the event one vector is a superset of the other, the size is used.
+     */
+    bool operator<(const ByteVector &v) const;
+
+    /*!
+     * Returns true if this ByteVector is greater than \a v.
+     */
+    bool operator>(const ByteVector &v) const;
+
+    /*!
+     * Returns a vector that is \a v appended to this vector.
+     */
+    ByteVector operator+(const ByteVector &v) const;
+
+    /*!
+     * Copies ByteVector \a v.
+     */
+    ByteVector &operator=(const ByteVector &v);
+
+    /*!
+     * Copies ByteVector \a v.
+     */
+    ByteVector &operator=(char c);
+
+    /*!
+     * Copies ByteVector \a v.
+     */
+    ByteVector &operator=(const char *data);
+
+    /*!
+     * A static, empty ByteVector which is convenient and fast (since returning
+     * an empty or "null" value does not require instantiating a new ByteVector).
+     */
+    static ByteVector null;
+
+  protected:
+    /*
+     * If this ByteVector is being shared via implicit sharing, do a deep copy
+     * of the data and separate from the shared members.  This should be called
+     * by all non-const subclass members.
+     */
+    void detach();
+
+  private:
+    class ByteVectorPrivate;
+    ByteVectorPrivate *d;
+  };
+
+}
+
+/*!
+ * \relates TagLib::ByteVector
+ * Streams the ByteVector \a v to the output stream \a s.
+ */
+TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v);
+
+#endif
diff --git a/src/taglib/toolkit/tbytevectorlist.cpp b/src/taglib/toolkit/tbytevectorlist.cpp
new file mode 100644 (file)
index 0000000..b0553d2
--- /dev/null
@@ -0,0 +1,102 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "tbytevectorlist.h"
+
+using namespace TagLib;
+
+class ByteVectorListPrivate
+{
+
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &pattern,
+                                     int byteAlign)
+{
+  return split(v, pattern, byteAlign, 0);
+}
+
+ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &pattern,
+                                     int byteAlign, int max)
+{
+  ByteVectorList l;
+
+  uint previousOffset = 0;
+  for(int offset = v.find(pattern, 0, byteAlign);
+      offset != -1 && (max == 0 || max > int(l.size()) + 1);
+      offset = v.find(pattern, offset + pattern.size(), byteAlign))
+  {
+    if(offset - previousOffset >= 1)
+      l.append(v.mid(previousOffset, offset - previousOffset));
+    else
+      l.append(ByteVector::null);
+
+    previousOffset = offset + pattern.size();
+  }
+
+  if(previousOffset < v.size())
+    l.append(v.mid(previousOffset, v.size() - previousOffset));
+
+  return l;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ByteVectorList::ByteVectorList() : List<ByteVector>()
+{
+
+}
+
+ByteVectorList::ByteVectorList(const ByteVectorList &l) : List<ByteVector>(l)
+{
+
+}
+
+ByteVectorList::~ByteVectorList()
+{
+
+}
+
+ByteVector ByteVectorList::toByteVector(const ByteVector &separator) const
+{
+  ByteVector v;
+
+  ConstIterator it = begin();
+
+  while(it != end()) {
+    v.append(*it);
+    it++;
+    if(it != end())
+      v.append(separator);
+  }
+
+  return v;
+}
diff --git a/src/taglib/toolkit/tbytevectorlist.h b/src/taglib/toolkit/tbytevectorlist.h
new file mode 100644 (file)
index 0000000..8d84710
--- /dev/null
@@ -0,0 +1,91 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_BYTEVECTORLIST_H
+#define TAGLIB_BYTEVECTORLIST_H
+
+#include "taglib_export.h"
+#include "tbytevector.h"
+#include "tlist.h"
+
+namespace TagLib {
+
+  //! A list of ByteVectors
+
+  /*!
+   * A List specialization with some handy features useful for ByteVectors.
+   */
+
+  class TAGLIB_EXPORT ByteVectorList : public List<ByteVector>
+  {
+  public:
+
+    /*!
+     * Construct an empty ByteVectorList.
+     */
+    ByteVectorList();
+
+    /*!
+     * Destroys this ByteVectorList instance.
+     */
+    virtual ~ByteVectorList();
+
+    /*!
+     * Make a shallow, implicitly shared, copy of \a l.  Because this is
+     * implicitly shared, this method is lightweight and suitable for
+     * pass-by-value usage.
+     */
+    ByteVectorList(const ByteVectorList &l);
+
+    /*!
+     * Convert the ByteVectorList to a ByteVector separated by \a separator.  By
+     * default a space is used.
+     */
+    ByteVector toByteVector(const ByteVector &separator = " ") const;
+
+    /*!
+     * Splits the ByteVector \a v into several strings at \a pattern.  This will
+     * not include the pattern in the returned ByteVectors.
+     */
+    static ByteVectorList split(const ByteVector &v, const ByteVector &pattern,
+                                int byteAlign = 1);
+    /*!
+     * Splits the ByteVector \a v into several strings at \a pattern.  This will
+     * not include the pattern in the returned ByteVectors.  \a max is the
+     * maximum number of entries that will be separated.  If \a max for instance
+     * is 2 then a maximum of 1 match will be found and the vector will be split
+     * on that match.
+     */
+    // BIC: merge with the function above
+    static ByteVectorList split(const ByteVector &v, const ByteVector &pattern,
+                                int byteAlign, int max);
+  private:
+    class ByteVectorListPrivate;
+    ByteVectorListPrivate *d;
+  };
+
+}
+
+#endif
diff --git a/src/taglib/toolkit/tdebug.cpp b/src/taglib/toolkit/tdebug.cpp
new file mode 100644 (file)
index 0000000..341bb37
--- /dev/null
@@ -0,0 +1,55 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <iostream>
+#include <bitset>
+
+#include "tdebug.h"
+#include "tstring.h"
+
+using namespace TagLib;
+
+#ifndef NDEBUG
+void TagLib::debug(const String &s)
+{
+  std::cerr << "TagLib: " << s << std::endl;
+}
+
+void TagLib::debugData(const ByteVector &v)
+{
+  for(uint i = 0; i < v.size(); i++) {
+
+    std::cout << "*** [" << i << "] - '" << char(v[i]) << "' - int " << int(v[i])
+              << std::endl;
+
+    std::bitset<8> b(v[i]);
+
+    for(int j = 0; j < 8; j++)
+      std::cout << i << ":" << j << " " << b.test(j) << std::endl;
+
+    std::cout << std::endl;
+  }
+}
+#endif
diff --git a/src/taglib/toolkit/tdebug.h b/src/taglib/toolkit/tdebug.h
new file mode 100644 (file)
index 0000000..a932b26
--- /dev/null
@@ -0,0 +1,71 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_DEBUG_H
+#define TAGLIB_DEBUG_H
+
+namespace TagLib {
+
+  class String;
+  class ByteVector;
+
+#ifndef DO_NOT_DOCUMENT
+#ifndef NDEBUG
+
+  /*!
+   * A simple function that prints debugging output to cerr if debugging is
+   * not disabled.
+   *
+   * \warning Do not use this outside of TagLib, it could lead to undefined
+   * symbols in your build if TagLib is built with NDEBUG defined and your
+   * application is not.
+   *
+   * \internal
+   */
+  void debug(const String &s);
+
+  /*!
+   * For debugging binary data.
+   *
+   * \warning Do not use this outside of TagLib, it could lead to undefined
+   * symbols in your build if TagLib is built with NDEBUG defined and your
+   * application is not.
+   *
+   * \internal
+   */
+  void debugData(const ByteVector &v);
+
+#else
+
+  // Define these to an empty statement if debugging is disabled.
+
+#define debug(x)
+#define debugData(x)
+
+#endif
+#endif
+}
+
+#endif
diff --git a/src/taglib/toolkit/tfile.cpp b/src/taglib/toolkit/tfile.cpp
new file mode 100644 (file)
index 0000000..7090a64
--- /dev/null
@@ -0,0 +1,564 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "tfile.h"
+#include "tstring.h"
+#include "tdebug.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <wchar.h>
+# include <windows.h>
+# include <io.h>
+# define ftruncate _chsize
+#else
+# include <unistd.h>
+#endif
+
+#include <stdlib.h>
+
+#ifndef R_OK
+# define R_OK 4
+#endif
+#ifndef W_OK
+# define W_OK 2
+#endif
+
+using namespace TagLib;
+
+#ifdef _WIN32
+
+typedef FileName FileNameHandle;
+
+#else
+
+struct FileNameHandle : public std::string
+{
+  FileNameHandle(FileName name) : std::string(name) {}
+  operator FileName () const { return c_str(); }
+};
+
+#endif
+
+class File::FilePrivate
+{
+public:
+  FilePrivate(FileName fileName);
+
+  FILE *file;
+
+  FileNameHandle name;
+
+  bool readOnly;
+  bool valid;
+  ulong size;
+  static const uint bufferSize = 1024;
+};
+
+File::FilePrivate::FilePrivate(FileName fileName) :
+  file(0),
+  name(fileName),
+  readOnly(true),
+  valid(true),
+  size(0)
+{
+  // First try with read / write mode, if that fails, fall back to read only.
+
+#ifdef _WIN32
+
+  if(wcslen((const wchar_t *) fileName) > 0) {
+
+    file = _wfopen(name, L"rb+");
+
+    if(file)
+      readOnly = false;
+    else
+      file = _wfopen(name, L"rb");
+
+    if(file)
+      return;
+
+  }
+
+#endif
+
+  file = fopen(name, "rb+");
+
+  if(file)
+    readOnly = false;
+  else
+    file = fopen(name, "rb");
+
+  if(!file)
+    debug("Could not open file " + String((const char *) name));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+File::File(FileName file)
+{
+  d = new FilePrivate(file);
+}
+
+File::~File()
+{
+  if(d->file)
+    fclose(d->file);
+  delete d;
+}
+
+FileName File::name() const
+{
+  return d->name;
+}
+
+ByteVector File::readBlock(ulong length)
+{
+  if(!d->file) {
+    debug("File::readBlock() -- Invalid File");
+    return ByteVector::null;
+  }
+
+  if(length == 0)
+    return ByteVector::null;
+
+  if(length > FilePrivate::bufferSize &&
+     length > ulong(File::length()))
+  {
+    length = File::length();
+  }
+
+  ByteVector v(static_cast<uint>(length));
+  const int count = fread(v.data(), sizeof(char), length, d->file);
+  v.resize(count);
+  return v;
+}
+
+void File::writeBlock(const ByteVector &data)
+{
+  if(!d->file)
+    return;
+
+  if(d->readOnly) {
+    debug("File::writeBlock() -- attempted to write to a file that is not writable");
+    return;
+  }
+
+  fwrite(data.data(), sizeof(char), data.size(), d->file);
+}
+
+long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
+{
+  if(!d->file || pattern.size() > d->bufferSize)
+      return -1;
+
+  // The position in the file that the current buffer starts at.
+
+  long bufferOffset = fromOffset;
+  ByteVector buffer;
+
+  // These variables are used to keep track of a partial match that happens at
+  // the end of a buffer.
+
+  int previousPartialMatch = -1;
+  int beforePreviousPartialMatch = -1;
+
+  // Save the location of the current read pointer.  We will restore the
+  // position using seek() before all returns.
+
+  long originalPosition = tell();
+
+  // Start the search at the offset.
+
+  seek(fromOffset);
+
+  // This loop is the crux of the find method.  There are three cases that we
+  // want to account for:
+  //
+  // (1) The previously searched buffer contained a partial match of the search
+  // pattern and we want to see if the next one starts with the remainder of
+  // that pattern.
+  //
+  // (2) The search pattern is wholly contained within the current buffer.
+  //
+  // (3) The current buffer ends with a partial match of the pattern.  We will
+  // note this for use in the next itteration, where we will check for the rest
+  // of the pattern.
+  //
+  // All three of these are done in two steps.  First we check for the pattern
+  // and do things appropriately if a match (or partial match) is found.  We
+  // then check for "before".  The order is important because it gives priority
+  // to "real" matches.
+
+  for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
+
+    // (1) previous partial match
+
+    if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) {
+      const int patternOffset = (d->bufferSize - previousPartialMatch);
+      if(buffer.containsAt(pattern, 0, patternOffset)) {
+        seek(originalPosition);
+        return bufferOffset - d->bufferSize + previousPartialMatch;
+      }
+    }
+
+    if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) {
+      const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch);
+      if(buffer.containsAt(before, 0, beforeOffset)) {
+        seek(originalPosition);
+        return -1;
+      }
+    }
+
+    // (2) pattern contained in current buffer
+
+    long location = buffer.find(pattern);
+    if(location >= 0) {
+      seek(originalPosition);
+      return bufferOffset + location;
+    }
+
+    if(!before.isNull() && buffer.find(before) >= 0) {
+      seek(originalPosition);
+      return -1;
+    }
+
+    // (3) partial match
+
+    previousPartialMatch = buffer.endsWithPartialMatch(pattern);
+
+    if(!before.isNull())
+      beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);
+
+    bufferOffset += d->bufferSize;
+  }
+
+  // Since we hit the end of the file, reset the status before continuing.
+
+  clear();
+
+  seek(originalPosition);
+
+  return -1;
+}
+
+
+long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
+{
+  if(!d->file || pattern.size() > d->bufferSize)
+      return -1;
+
+  // The position in the file that the current buffer starts at.
+
+  ByteVector buffer;
+
+  // These variables are used to keep track of a partial match that happens at
+  // the end of a buffer.
+
+  /*
+  int previousPartialMatch = -1;
+  int beforePreviousPartialMatch = -1;
+  */
+
+  // Save the location of the current read pointer.  We will restore the
+  // position using seek() before all returns.
+
+  long originalPosition = tell();
+
+  // Start the search at the offset.
+
+  long bufferOffset;
+  if(fromOffset == 0) {
+    seek(-1 * int(d->bufferSize), End);
+    bufferOffset = tell();
+  }
+  else {
+    seek(fromOffset + -1 * int(d->bufferSize), Beginning);
+    bufferOffset = tell();
+  }
+
+  // See the notes in find() for an explanation of this algorithm.
+
+  for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
+
+    // TODO: (1) previous partial match
+
+    // (2) pattern contained in current buffer
+
+    long location = buffer.rfind(pattern);
+    if(location >= 0) {
+      seek(originalPosition);
+      return bufferOffset + location;
+    }
+
+    if(!before.isNull() && buffer.find(before) >= 0) {
+      seek(originalPosition);
+      return -1;
+    }
+
+    // TODO: (3) partial match
+
+    bufferOffset -= d->bufferSize;
+    seek(bufferOffset);
+  }
+
+  // Since we hit the end of the file, reset the status before continuing.
+
+  clear();
+
+  seek(originalPosition);
+
+  return -1;
+}
+
+void File::insert(const ByteVector &data, ulong start, ulong replace)
+{
+  if(!d->file)
+    return;
+
+  if(data.size() == replace) {
+    seek(start);
+    writeBlock(data);
+    return;
+  }
+  else if(data.size() < replace) {
+      seek(start);
+      writeBlock(data);
+      removeBlock(start + data.size(), replace - data.size());
+      return;
+  }
+
+  // Woohoo!  Faster (about 20%) than id3lib at last.  I had to get hardcore
+  // and avoid TagLib's high level API for rendering just copying parts of
+  // the file that don't contain tag data.
+  //
+  // Now I'll explain the steps in this ugliness:
+
+  // First, make sure that we're working with a buffer that is longer than
+  // the *differnce* in the tag sizes.  We want to avoid overwriting parts
+  // that aren't yet in memory, so this is necessary.
+
+  ulong bufferLength = bufferSize();
+
+  while(data.size() - replace > bufferLength)
+    bufferLength += bufferSize();
+
+  // Set where to start the reading and writing.
+
+  long readPosition = start + replace;
+  long writePosition = start;
+
+  ByteVector buffer;
+  ByteVector aboutToOverwrite(static_cast<uint>(bufferLength));
+
+  // This is basically a special case of the loop below.  Here we're just
+  // doing the same steps as below, but since we aren't using the same buffer
+  // size -- instead we're using the tag size -- this has to be handled as a
+  // special case.  We're also using File::writeBlock() just for the tag.
+  // That's a bit slower than using char *'s so, we're only doing it here.
+
+  seek(readPosition);
+  int bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
+  readPosition += bufferLength;
+
+  seek(writePosition);
+  writeBlock(data);
+  writePosition += data.size();
+
+  buffer = aboutToOverwrite;
+
+  // In case we've already reached the end of file...
+
+  buffer.resize(bytesRead);
+
+  // Ok, here's the main loop.  We want to loop until the read fails, which
+  // means that we hit the end of the file.
+
+  while(!buffer.isEmpty()) {
+
+    // Seek to the current read position and read the data that we're about
+    // to overwrite.  Appropriately increment the readPosition.
+
+    seek(readPosition);
+    bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
+    aboutToOverwrite.resize(bytesRead);
+    readPosition += bufferLength;
+
+    // Check to see if we just read the last block.  We need to call clear()
+    // if we did so that the last write succeeds.
+
+    if(ulong(bytesRead) < bufferLength)
+      clear();
+
+    // Seek to the write position and write our buffer.  Increment the
+    // writePosition.
+
+    seek(writePosition);
+    fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);
+    writePosition += buffer.size();
+
+    // Make the current buffer the data that we read in the beginning.
+
+    buffer = aboutToOverwrite;
+
+    // Again, we need this for the last write.  We don't want to write garbage
+    // at the end of our file, so we need to set the buffer size to the amount
+    // that we actually read.
+
+    bufferLength = bytesRead;
+  }
+}
+
+void File::removeBlock(ulong start, ulong length)
+{
+  if(!d->file)
+    return;
+
+  ulong bufferLength = bufferSize();
+
+  long readPosition = start + length;
+  long writePosition = start;
+
+  ByteVector buffer(static_cast<uint>(bufferLength));
+
+  ulong bytesRead = 1;
+
+  while(bytesRead != 0) {
+    seek(readPosition);
+    bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file);
+    readPosition += bytesRead;
+
+    // Check to see if we just read the last block.  We need to call clear()
+    // if we did so that the last write succeeds.
+
+    if(bytesRead < bufferLength)
+      clear();
+
+    seek(writePosition);
+    fwrite(buffer.data(), sizeof(char), bytesRead, d->file);
+    writePosition += bytesRead;
+  }
+  truncate(writePosition);
+}
+
+bool File::readOnly() const
+{
+  return d->readOnly;
+}
+
+bool File::isReadable(const char *file)
+{
+  return access(file, R_OK) == 0;
+}
+
+bool File::isOpen() const
+{
+  return (d->file != NULL);
+}
+
+bool File::isValid() const
+{
+  return isOpen() && d->valid;
+}
+
+void File::seek(long offset, Position p)
+{
+  if(!d->file) {
+    debug("File::seek() -- trying to seek in a file that isn't opened.");
+    return;
+  }
+
+  switch(p) {
+  case Beginning:
+    fseek(d->file, offset, SEEK_SET);
+    break;
+  case Current:
+    fseek(d->file, offset, SEEK_CUR);
+    break;
+  case End:
+    fseek(d->file, offset, SEEK_END);
+    break;
+  }
+}
+
+void File::clear()
+{
+  clearerr(d->file);
+}
+
+long File::tell() const
+{
+  return ftell(d->file);
+}
+
+long File::length()
+{
+  // Do some caching in case we do multiple calls.
+
+  if(d->size > 0)
+    return d->size;
+
+  if(!d->file)
+    return 0;
+
+  long curpos = tell();
+
+  seek(0, End);
+  long endpos = tell();
+
+  seek(curpos, Beginning);
+
+  d->size = endpos;
+  return endpos;
+}
+
+bool File::isWritable(const char *file)
+{
+  return access(file, W_OK) == 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void File::setValid(bool valid)
+{
+  d->valid = valid;
+}
+
+void File::truncate(long length)
+{
+  ftruncate(fileno(d->file), length);
+}
+
+TagLib::uint File::bufferSize()
+{
+  return FilePrivate::bufferSize;
+}
diff --git a/src/taglib/toolkit/tfile.h b/src/taglib/toolkit/tfile.h
new file mode 100644 (file)
index 0000000..16cc73b
--- /dev/null
@@ -0,0 +1,270 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_FILE_H
+#define TAGLIB_FILE_H
+
+#include "taglib_export.h"
+#include "taglib.h"
+#include "tbytevector.h"
+
+namespace TagLib {
+
+  class String;
+  class Tag;
+  class AudioProperties;
+
+#ifdef _WIN32
+  class TAGLIB_EXPORT FileName
+  {
+  public:
+    FileName(const wchar_t *name) : m_wname(name) {}
+    FileName(const char *name) : m_name(name) {}
+    operator const wchar_t *() const { return m_wname.c_str(); }
+    operator const char *() const { return m_name.c_str(); }
+  private:
+    std::string m_name;
+    std::wstring m_wname;
+  };
+#else
+  typedef const char *FileName;
+#endif
+
+  //! A file class with some useful methods for tag manipulation
+
+  /*!
+   * This class is a basic file class with some methods that are particularly
+   * useful for tag editors.  It has methods to take advantage of
+   * ByteVector and a binary search method for finding patterns in a file.
+   */
+
+  class TAGLIB_EXPORT File
+  {
+  public:
+    /*!
+     * Position in the file used for seeking.
+     */
+    enum Position {
+      //! Seek from the beginning of the file.
+      Beginning,
+      //! Seek from the current position in the file.
+      Current,
+      //! Seek from the end of the file.
+      End
+    };
+
+    /*!
+     * Destroys this File instance.
+     */
+    virtual ~File();
+
+    /*!
+     * Returns the file name in the local file system encoding.
+     */
+    FileName name() const;
+
+    /*!
+     * Returns a pointer to this file's tag.  This should be reimplemented in
+     * the concrete subclasses.
+     */
+    virtual Tag *tag() const = 0;
+
+    /*!
+     * Returns a pointer to this file's audio properties.  This should be
+     * reimplemented in the concrete subclasses.  If no audio properties were
+     * read then this will return a null pointer.
+     */
+    virtual AudioProperties *audioProperties() const = 0;
+
+    /*!
+     * Save the file and its associated tags.  This should be reimplemented in
+     * the concrete subclasses.  Returns true if the save succeeds.
+     *
+     * \warning On UNIX multiple processes are able to write to the same file at
+     * the same time.  This can result in serious file corruption.  If you are
+     * developing a program that makes use of TagLib from multiple processes you
+     * must insure that you are only doing writes to a particular file from one
+     * of them.
+     */
+    virtual bool save() = 0;
+
+    /*!
+     * Reads a block of size \a length at the current get pointer.
+     */
+    ByteVector readBlock(ulong length);
+
+    /*!
+     * Attempts to write the block \a data at the current get pointer.  If the
+     * file is currently only opened read only -- i.e. readOnly() returns true --
+     * this attempts to reopen the file in read/write mode.
+     *
+     * \note This should be used instead of using the streaming output operator
+     * for a ByteVector.  And even this function is significantly slower than
+     * doing output with a char[].
+     */
+    void writeBlock(const ByteVector &data);
+
+    /*!
+     * Returns the offset in the file that \a pattern occurs at or -1 if it can
+     * not be found.  If \a before is set, the search will only continue until the
+     * pattern \a before is found.  This is useful for tagging purposes to search
+     * for a tag before the synch frame.
+     *
+     * Searching starts at \a fromOffset, which defaults to the beginning of the
+     * file.
+     *
+     * \note This has the practial limitation that \a pattern can not be longer
+     * than the buffer size used by readBlock().  Currently this is 1024 bytes.
+     */
+    long find(const ByteVector &pattern,
+              long fromOffset = 0,
+              const ByteVector &before = ByteVector::null);
+
+    /*!
+     * Returns the offset in the file that \a pattern occurs at or -1 if it can
+     * not be found.  If \a before is set, the search will only continue until the
+     * pattern \a before is found.  This is useful for tagging purposes to search
+     * for a tag before the synch frame.
+     *
+     * Searching starts at \a fromOffset and proceeds from the that point to the
+     * beginning of the file and defaults to the end of the file.
+     *
+     * \note This has the practial limitation that \a pattern can not be longer
+     * than the buffer size used by readBlock().  Currently this is 1024 bytes.
+     */
+    long rfind(const ByteVector &pattern,
+               long fromOffset = 0,
+               const ByteVector &before = ByteVector::null);
+
+    /*!
+     * Insert \a data at position \a start in the file overwriting \a replace
+     * bytes of the original content.
+     *
+     * \note This method is slow since it requires rewriting all of the file
+     * after the insertion point.
+     */
+    void insert(const ByteVector &data, ulong start = 0, ulong replace = 0);
+
+    /*!
+     * Removes a block of the file starting a \a start and continuing for
+     * \a length bytes.
+     *
+     * \note This method is slow since it involves rewriting all of the file
+     * after the removed portion.
+     */
+    void removeBlock(ulong start = 0, ulong length = 0);
+
+    /*!
+     * Returns true if the file is read only (or if the file can not be opened).
+     */
+    bool readOnly() const;
+
+    /*!
+     * Since the file can currently only be opened as an argument to the
+     * constructor (sort-of by design), this returns if that open succeeded.
+     */
+    bool isOpen() const;
+
+    /*!
+     * Returns true if the file is open and readble.
+     */
+    bool isValid() const;
+
+    /*!
+     * Move the I/O pointer to \a offset in the file from position \a p.  This
+     * defaults to seeking from the beginning of the file.
+     *
+     * \see Position
+     */
+    void seek(long offset, Position p = Beginning);
+
+    /*!
+     * Reset the end-of-file and error flags on the file.
+     */
+    void clear();
+
+    /*!
+     * Returns the current offset within the file.
+     */
+    long tell() const;
+
+    /*!
+     * Returns the length of the file.
+     */
+    long length();
+
+    /*!
+     * Returns true if \a file can be opened for reading.  If the file does not
+     * exist, this will return false.
+     *
+     * \deprecated
+     */
+    static bool isReadable(const char *file);
+
+    /*!
+     * Returns true if \a file can be opened for writing.
+     *
+     * \deprecated
+     */
+    static bool isWritable(const char *name);
+
+  protected:
+    /*!
+     * Construct a File object and opens the \a file.  \a file should be a
+     * be a C-string in the local file system encoding.
+     *
+     * \note Constructor is protected since this class should only be
+     * instantiated through subclasses.
+     */
+    File(FileName file);
+
+    /*!
+     * Marks the file as valid or invalid.
+     *
+     * \see isValid()
+     */
+    void setValid(bool valid);
+
+    /*!
+     * Truncates the file to a \a length.
+     */
+    void truncate(long length);
+
+    /*!
+     * Returns the buffer size that is used for internal buffering.
+     */
+    static uint bufferSize();
+
+  private:
+    File(const File &);
+    File &operator=(const File &);
+
+    class FilePrivate;
+    FilePrivate *d;
+  };
+
+}
+
+#endif
diff --git a/src/taglib/toolkit/tlist.h b/src/taglib/toolkit/tlist.h
new file mode 100644 (file)
index 0000000..70be324
--- /dev/null
@@ -0,0 +1,252 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_LIST_H
+#define TAGLIB_LIST_H
+
+#include "taglib.h"
+
+#include <list>
+
+namespace TagLib {
+
+  //! A generic, implicitly shared list.
+
+  /*!
+   * This is basic generic list that's somewhere between a std::list and a
+   * QValueList.  This class is implicitly shared.  For example:
+   *
+   * \code
+   *
+   * TagLib::List<int> l = someOtherIntList;
+   *
+   * \endcode
+   *
+   * The above example is very cheap.  This also makes lists suitable for the
+   * return types of functions.  The above example will just copy a pointer rather
+   * than copying the data in the list.  When your \e shared list's data changes,
+   * only \e then will the data be copied.
+   */
+
+  template <class T> class List
+  {
+  public:
+#ifndef DO_NOT_DOCUMENT
+    typedef typename std::list<T>::iterator Iterator;
+    typedef typename std::list<T>::const_iterator ConstIterator;
+#endif
+
+    /*!
+     * Constructs an empty list.
+     */
+    List();
+
+    /*!
+     * Make a shallow, implicitly shared, copy of \a l.  Because this is
+     * implicitly shared, this method is lightweight and suitable for
+     * pass-by-value usage.
+     */
+    List(const List<T> &l);
+
+    /*!
+     * Destroys this List instance.  If auto deletion is enabled and this list
+     * contains a pointer type all of the memebers are also deleted.
+     */
+    virtual ~List();
+
+    /*!
+     * Returns an STL style iterator to the beginning of the list.  See
+     * std::list::const_iterator for the semantics.
+     */
+    Iterator begin();
+
+    /*!
+     * Returns an STL style constant iterator to the beginning of the list.  See
+     * std::list::iterator for the semantics.
+     */
+    ConstIterator begin() const;
+
+    /*!
+     * Returns an STL style iterator to the end of the list.  See
+     * std::list::iterator for the semantics.
+     */
+    Iterator end();
+
+    /*!
+     * Returns an STL style constant iterator to the end of the list.  See
+     * std::list::const_iterator for the semantics.
+     */
+    ConstIterator end() const;
+
+    /*!
+     * Inserts a copy of \a value before \a it.
+     */
+    Iterator insert(Iterator it, const T &value);
+
+    /*!
+     * Inserts the \a value into the list.  This assumes that the list is
+     * currently sorted.  If \a unique is true then the value will not
+     * be inserted if it is already in the list.
+     */
+    List<T> &sortedInsert(const T &value, bool unique = false);
+
+    /*!
+     * Appends \a item to the end of the list and returns a reference to the
+     * list.
+     */
+    List<T> &append(const T &item);
+
+    /*!
+     * Appends all of the values in \a l to the end of the list and returns a
+     * reference to the list.
+     */
+    List<T> &append(const List<T> &l);
+
+    /*!
+     * Prepends \a item to the beginning list and returns a reference to the
+     * list.
+     */
+    List<T> &prepend(const T &item);
+
+    /*!
+     * Prepends all of the items in \a l to the beginning list and returns a
+     * reference to the list.
+     */
+    List<T> &prepend(const List<T> &l);
+
+    /*!
+     * Clears the list.  If auto deletion is enabled and this list contains a
+     * pointer type the members are also deleted.
+     *
+     * \see setAutoDelete()
+     */
+    List<T> &clear();
+
+    /*!
+     * Returns the number of elements in the list.
+     */
+    uint size() const;
+    bool isEmpty() const;
+
+    /*!
+     * Find the first occurrence of \a value.
+     */
+    Iterator find(const T &value);
+
+    /*!
+     * Find the first occurrence of \a value.
+     */
+    ConstIterator find(const T &value) const;
+
+    /*!
+     * Returns true if the list contains \a value.
+     */
+    bool contains(const T &value) const;
+
+    /*!
+     * Erase the item at \a it from the list.
+     */
+    Iterator erase(Iterator it);
+
+    /*!
+     * Returns a reference to the first item in the list.
+     */
+    const T &front() const;
+
+    /*!
+     * Returns a reference to the first item in the list.
+     */
+    T &front();
+
+    /*!
+     * Returns a reference to the last item in the list.
+     */
+    const T &back() const;
+
+    /*!
+     * Returns a reference to the last item in the list.
+     */
+    T &back();
+
+    /*!
+     * Auto delete the members of the list when the last reference to the list
+     * passes out of scope.  This will have no effect on lists which do not
+     * contain a pointer type.
+     *
+     * \note This relies on partial template instantiation -- most modern C++
+     * compilers should now support this.
+     */
+    void setAutoDelete(bool autoDelete);
+
+    /*!
+     * Returns a reference to item \a i in the list.
+     *
+     * \warning This method is slow.  Use iterators to loop through the list.
+     */
+    T &operator[](uint i);
+
+    /*!
+     * Returns a const reference to item \a i in the list.
+     *
+     * \warning This method is slow.  Use iterators to loop through the list.
+     */
+    const T &operator[](uint i) const;
+
+    /*!
+     * Make a shallow, implicitly shared, copy of \a l.  Because this is
+     * implicitly shared, this method is lightweight and suitable for
+     * pass-by-value usage.
+     */
+    List<T> &operator=(const List<T> &l);
+
+    /*!
+     * Compares this list with \a l and returns true if all of the elements are
+     * the same.
+     */
+    bool operator==(const List<T> &l) const;
+
+  protected:
+    /*
+     * If this List is being shared via implicit sharing, do a deep copy of the
+     * data and separate from the shared members.  This should be called by all
+     * non-const subclass members.
+     */
+    void detach();
+
+  private:
+#ifndef DO_NOT_DOCUMENT
+    template <class TP> class ListPrivate;
+    ListPrivate<T> *d;
+#endif
+  };
+
+}
+
+// Since GCC doesn't support the "export" keyword, we have to include the
+// implementation.
+
+#include "tlist.tcc"
+
+#endif
diff --git a/src/taglib/toolkit/tlist.tcc b/src/taglib/toolkit/tlist.tcc
new file mode 100644 (file)
index 0000000..cbc328b
--- /dev/null
@@ -0,0 +1,316 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <algorithm>
+
+namespace TagLib {
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+// The functionality of List<T>::setAutoDelete() is implemented here partial
+// template specialization.  This is implemented in such a way that calling
+// setAutoDelete() on non-pointer types will simply have no effect.
+
+// A base for the generic and specialized private class types.  New
+// non-templatized members should be added here.
+
+class ListPrivateBase : public RefCounter
+{
+public:
+  ListPrivateBase() : autoDelete(false) {}
+  bool autoDelete;
+};
+
+// A generic implementation
+
+template <class T>
+template <class TP> class List<T>::ListPrivate  : public ListPrivateBase
+{
+public:
+  ListPrivate() : ListPrivateBase() {}
+  ListPrivate(const std::list<TP> &l) : ListPrivateBase(), list(l) {}
+  void clear() {
+    list.clear();
+  }
+  std::list<TP> list;
+};
+
+// A partial specialization for all pointer types that implements the
+// setAutoDelete() functionality.
+
+template <class T>
+template <class TP> class List<T>::ListPrivate<TP *>  : public ListPrivateBase
+{
+public:
+  ListPrivate() : ListPrivateBase() {}
+  ListPrivate(const std::list<TP *> &l) : ListPrivateBase(), list(l) {}
+  ~ListPrivate() {
+    clear();
+  }
+  void clear() {
+    if(autoDelete) {
+      typename std::list<TP *>::const_iterator it = list.begin();
+      for(; it != list.end(); ++it)
+        delete *it;
+    }
+    list.clear();
+  }
+  std::list<TP *> list;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+List<T>::List()
+{
+  d = new ListPrivate<T>;
+}
+
+template <class T>
+List<T>::List(const List<T> &l) : d(l.d)
+{
+  d->ref();
+}
+
+template <class T>
+List<T>::~List()
+{
+  if(d->deref())
+    delete d;
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::begin()
+{
+  detach();
+  return d->list.begin();
+}
+
+template <class T>
+typename List<T>::ConstIterator List<T>::begin() const
+{
+  return d->list.begin();
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::end()
+{
+  detach();
+  return d->list.end();
+}
+
+template <class T>
+typename List<T>::ConstIterator List<T>::end() const
+{
+  return d->list.end();
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::insert(Iterator it, const T &item)
+{
+  detach();
+  return d->list.insert(it, item);
+}
+
+template <class T>
+List<T> &List<T>::sortedInsert(const T &value, bool unique)
+{
+  detach();
+  Iterator it = begin();
+  while(it != end() && *it < value)
+    ++it;
+  if(unique && it != end() && *it == value)
+    return *this;
+  insert(it, value);
+  return *this;
+}
+
+template <class T>
+List<T> &List<T>::append(const T &item)
+{
+  detach();
+  d->list.push_back(item);
+  return *this;
+}
+
+template <class T>
+List<T> &List<T>::append(const List<T> &l)
+{
+  detach();
+  d->list.insert(d->list.end(), l.begin(), l.end());
+  return *this;
+}
+
+template <class T>
+List<T> &List<T>::prepend(const T &item)
+{
+  detach();
+  d->list.push_front(item);
+  return *this;
+}
+
+template <class T>
+List<T> &List<T>::prepend(const List<T> &l)
+{
+  detach();
+  d->list.insert(d->list.begin(), l.begin(), l.end());
+  return *this;
+}
+
+template <class T>
+List<T> &List<T>::clear()
+{
+  detach();
+  d->clear();
+  return *this;
+}
+
+template <class T>
+TagLib::uint List<T>::size() const
+{
+  return d->list.size();
+}
+
+template <class T>
+bool List<T>::isEmpty() const
+{
+  return d->list.empty();
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::find(const T &value)
+{
+  return std::find(d->list.begin(), d->list.end(), value);
+}
+
+template <class T>
+typename List<T>::ConstIterator List<T>::find(const T &value) const
+{
+  return std::find(d->list.begin(), d->list.end(), value);
+}
+
+template <class T>
+bool List<T>::contains(const T &value) const
+{
+  return std::find(d->list.begin(), d->list.end(), value) != d->list.end();
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::erase(Iterator it)
+{
+  return d->list.erase(it);
+}
+
+template <class T>
+const T &List<T>::front() const
+{
+  return d->list.front();
+}
+
+template <class T>
+T &List<T>::front()
+{
+  detach();
+  return d->list.front();
+}
+
+template <class T>
+const T &List<T>::back() const
+{
+  return d->list.back();
+}
+
+template <class T>
+void List<T>::setAutoDelete(bool autoDelete)
+{
+  d->autoDelete = autoDelete;
+}
+
+template <class T>
+T &List<T>::back()
+{
+  detach();
+  return d->list.back();
+}
+
+template <class T>
+T &List<T>::operator[](uint i)
+{
+  Iterator it = d->list.begin();
+
+  for(uint j = 0; j < i; j++)
+    ++it;
+
+  return *it;
+}
+
+template <class T>
+const T &List<T>::operator[](uint i) const
+{
+  ConstIterator it = d->list.begin();
+
+  for(uint j = 0; j < i; j++)
+    ++it;
+
+  return *it;
+}
+
+template <class T>
+List<T> &List<T>::operator=(const List<T> &l)
+{
+  if(&l == this)
+    return *this;
+
+  if(d->deref())
+    delete d;
+  d = l.d;
+  d->ref();
+  return *this;
+}
+
+template <class T>
+bool List<T>::operator==(const List<T> &l) const
+{
+  return d->list == l.d->list;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+void List<T>::detach()
+{
+  if(d->count() > 1) {
+    d->deref();
+    d = new ListPrivate<T>(d->list);
+  }
+}
+
+} // namespace TagLib
diff --git a/src/taglib/toolkit/tmap.h b/src/taglib/toolkit/tmap.h
new file mode 100644 (file)
index 0000000..3551b19
--- /dev/null
@@ -0,0 +1,200 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MAP_H
+#define TAGLIB_MAP_H
+
+#include <map>
+using namespace std;
+
+#include "taglib.h"
+
+namespace TagLib {
+
+  //! A generic, implicitly shared map.
+
+  /*!
+   * This implements a standard map container that associates a key with a value
+   * and has fast key-based lookups.  This map is also implicitly shared making
+   * it suitable for pass-by-value usage.
+   */
+
+  template <class Key, class T> class Map
+  {
+  public:
+#ifndef DO_NOT_DOCUMENT
+#ifdef WANT_CLASS_INSTANTIATION_OF_MAP
+    // Some STL implementations get snippy over the use of the
+    // class keyword to distinguish different templates; Sun Studio
+    // in particular finds multiple specializations in certain rare
+    // cases and complains about that. GCC doesn't seem to mind,
+    // and uses the typedefs further below without the class keyword.
+    // Not all the specializations of Map can use the class keyword
+    // (when T is not actually a class type), so don't apply this
+    // generally.
+    typedef typename std::map<class Key, class T>::iterator Iterator;
+    typedef typename std::map<class Key, class T>::const_iterator ConstIterator;
+#else
+    typedef typename std::map<Key, T>::iterator Iterator;
+    typedef typename std::map<Key, T>::const_iterator ConstIterator;
+#endif
+#endif
+
+    /*!
+     * Constructs an empty Map.
+     */
+    Map();
+
+    /*!
+     * Make a shallow, implicitly shared, copy of \a m.  Because this is
+     * implicitly shared, this method is lightweight and suitable for
+     * pass-by-value usage.
+     */
+    Map(const Map<Key, T> &m);
+
+    /*!
+     * Destroys this instance of the Map.
+     */
+    virtual ~Map();
+
+    /*!
+     * Returns an STL style iterator to the beginning of the map.  See
+     * std::map::iterator for the semantics.
+     */
+    Iterator begin();
+
+    /*!
+     * Returns an STL style iterator to the beginning of the map.  See
+     * std::map::const_iterator for the semantics.
+     */
+    ConstIterator begin() const;
+
+    /*!
+     * Returns an STL style iterator to the end of the map.  See
+     * std::map::iterator for the semantics.
+     */
+    Iterator end();
+
+    /*!
+     * Returns an STL style iterator to the end of the map.  See
+     * std::map::const_iterator for the semantics.
+     */
+    ConstIterator end() const;
+
+    /*!
+     * Inserts \a value under \a key in the map.  If a value for \a key already
+     * exists it will be overwritten.
+     */
+    Map<Key, T> &insert(const Key &key, const T &value);
+
+    /*!
+     * Removes all of the elements from elements from the map.  This however
+     * will not delete pointers if the mapped type is a pointer type.
+     */
+    Map<Key, T> &clear();
+
+    /*!
+     * The number of elements in the map.
+     *
+     * \see isEmpty()
+     */
+    uint size() const;
+
+    /*!
+     * Returns true if the map is empty.
+     *
+     * \see size()
+     */
+    bool isEmpty() const;
+
+    /*!
+     * Find the first occurrence of \a key.
+     */
+    Iterator find(const Key &key);
+
+    /*!
+     * Find the first occurrence of \a key.
+     */
+    ConstIterator find(const Key &key) const;
+
+    /*!
+     * Returns true if the map contains an instance of \a key.
+     */
+    bool contains(const Key &key) const;
+
+    /*!
+     * Erase the item at \a it from the list.
+     */
+    Map<Key, T> &erase(Iterator it);
+
+    /*!
+     * Erase the item with \a key from the list.
+     */
+    Map<Key, T> &erase(const Key &key);
+
+    /*!
+     * Returns a reference to the value associated with \a key.
+     *
+     * \note This has undefined behavior if the key is not present in the map.
+     */
+    const T &operator[](const Key &key) const;
+
+    /*!
+     * Returns a reference to the value associated with \a key.
+     *
+     * \note This has undefined behavior if the key is not present in the map.
+     */
+    T &operator[](const Key &key);
+
+    /*!
+     * Make a shallow, implicitly shared, copy of \a m.  Because this is
+     * implicitly shared, this method is lightweight and suitable for
+     * pass-by-value usage.
+     */
+    Map<Key, T> &operator=(const Map<Key, T> &m);
+
+  protected:
+    /*
+     * If this List is being shared via implicit sharing, do a deep copy of the
+     * data and separate from the shared members.  This should be called by all
+     * non-const subclass members.
+     */
+    void detach();
+
+  private:
+#ifndef DO_NOT_DOCUMENT
+    template <class KeyP, class TP> class MapPrivate;
+    MapPrivate<Key, T> *d;
+#endif
+  };
+
+}
+
+// Since GCC doesn't support the "export" keyword, we have to include the
+// implementation.
+
+#include "tmap.tcc"
+
+#endif
diff --git a/src/taglib/toolkit/tmap.tcc b/src/taglib/toolkit/tmap.tcc
new file mode 100644 (file)
index 0000000..6e689d3
--- /dev/null
@@ -0,0 +1,196 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+namespace TagLib {
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class Key, class T>
+template <class KeyP, class TP>
+class Map<Key, T>::MapPrivate : public RefCounter
+{
+public:
+  MapPrivate() : RefCounter() {}
+#ifdef WANT_CLASS_INSTANTIATION_OF_MAP
+  MapPrivate(const std::map<class KeyP, class TP>& m) : RefCounter(), map(m) {}
+  std::map<class KeyP, class TP> map;
+#else
+  MapPrivate(const std::map<KeyP, TP>& m) : RefCounter(), map(m) {}
+  std::map<KeyP, TP> map;
+#endif
+};
+
+template <class Key, class T>
+Map<Key, T>::Map()
+{
+  d = new MapPrivate<Key, T>;
+}
+
+template <class Key, class T>
+Map<Key, T>::Map(const Map<Key, T> &m) : d(m.d)
+{
+  d->ref();
+}
+
+template <class Key, class T>
+Map<Key, T>::~Map()
+{
+  if(d->deref())
+    delete(d);
+}
+
+template <class Key, class T>
+typename Map<Key, T>::Iterator Map<Key, T>::begin()
+{
+  detach();
+  return d->map.begin();
+}
+
+template <class Key, class T>
+typename Map<Key, T>::ConstIterator Map<Key, T>::begin() const
+{
+  return d->map.begin();
+}
+
+template <class Key, class T>
+typename Map<Key, T>::Iterator Map<Key, T>::end()
+{
+  detach();
+  return d->map.end();
+}
+
+template <class Key, class T>
+typename Map<Key, T>::ConstIterator Map<Key, T>::end() const
+{
+  return d->map.end();
+}
+
+template <class Key, class T>
+Map<Key, T> &Map<Key, T>::insert(const Key &key, const T &value)
+{
+  detach();
+  d->map[key] = value;
+  return *this;
+}
+
+template <class Key, class T>
+Map<Key, T> &Map<Key, T>::clear()
+{
+  detach();
+  d->map.clear();
+  return *this;
+}
+
+template <class Key, class T>
+bool Map<Key, T>::isEmpty() const
+{
+  return d->map.empty();
+}
+
+template <class Key, class T>
+typename Map<Key, T>::Iterator Map<Key, T>::find(const Key &key)
+{
+  detach();
+  return d->map.find(key);
+}
+
+template <class Key, class T>
+typename Map<Key,T>::ConstIterator Map<Key, T>::find(const Key &key) const
+{
+  return d->map.find(key);
+}
+
+template <class Key, class T>
+bool Map<Key, T>::contains(const Key &key) const
+{
+  return d->map.find(key) != d->map.end();
+}
+
+template <class Key, class T>
+Map<Key, T> &Map<Key,T>::erase(Iterator it)
+{
+  detach();
+  d->map.erase(it);
+  return *this;
+}
+
+template <class Key, class T>
+Map<Key, T> &Map<Key,T>::erase(const Key &key)
+{
+  detach();
+  Iterator it = d->map.find(key);
+  if(it != d->map.end())
+    d->map.erase(it);
+  return *this;
+}
+
+template <class Key, class T>
+TagLib::uint Map<Key, T>::size() const
+{
+  return d->map.size();
+}
+
+template <class Key, class T>
+const T &Map<Key, T>::operator[](const Key &key) const
+{
+  return d->map[key];
+}
+
+template <class Key, class T>
+T &Map<Key, T>::operator[](const Key &key)
+{
+  detach();
+  return d->map[key];
+}
+
+template <class Key, class T>
+Map<Key, T> &Map<Key, T>::operator=(const Map<Key, T> &m)
+{
+  if(&m == this)
+    return *this;
+
+  if(d->deref())
+    delete(d);
+  d = m.d;
+  d->ref();
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class Key, class T>
+void Map<Key, T>::detach()
+{
+  if(d->count() > 1) {
+    d->deref();
+    d = new MapPrivate<Key, T>(d->map);
+  }
+}
+
+} // namespace TagLib
diff --git a/src/taglib/toolkit/tstring.cpp b/src/taglib/toolkit/tstring.cpp
new file mode 100644 (file)
index 0000000..876575b
--- /dev/null
@@ -0,0 +1,811 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "tstring.h"
+#include "unicode.h"
+#include "tdebug.h"
+
+#include <iostream>
+
+#include <string.h>
+
+namespace TagLib {
+
+  inline unsigned short byteSwap(unsigned short x)
+  {
+    return (((x) >> 8) & 0xff) | (((x) & 0xff) << 8);
+  }
+
+  inline unsigned short combine(unsigned char c1, unsigned char c2)
+  {
+    return (c1 << 8) | c2;
+  }
+}
+
+using namespace TagLib;
+
+class String::StringPrivate : public RefCounter
+{
+public:
+  StringPrivate(const wstring &s) :
+    RefCounter(),
+    data(s),
+    CString(0) {}
+
+  StringPrivate() :
+    RefCounter(),
+    CString(0) {}
+
+  ~StringPrivate() {
+    delete [] CString;
+  }
+
+  wstring data;
+
+  /*!
+   * This is only used to hold the a pointer to the most recent value of
+   * toCString.
+   */
+  char *CString;
+};
+
+String String::null;
+
+////////////////////////////////////////////////////////////////////////////////
+
+String::String()
+{
+  d = new StringPrivate;
+}
+
+String::String(const String &s) : d(s.d)
+{
+  d->ref();
+}
+
+String::String(const std::string &s, Type t)
+{
+  d = new StringPrivate;
+
+  if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
+    debug("String::String() -- A std::string should not contain UTF16.");
+    return;
+  }
+
+  int length = s.length();
+  d->data.resize(length);
+  wstring::iterator targetIt = d->data.begin();
+
+  for(std::string::const_iterator it = s.begin(); it != s.end(); it++) {
+    *targetIt = uchar(*it);
+    ++targetIt;
+  }
+
+  prepare(t);
+}
+
+String::String(const wstring &s, Type t)
+{
+  d = new StringPrivate(s);
+  prepare(t);
+}
+
+String::String(const wchar_t *s, Type t)
+{
+  d = new StringPrivate(s);
+  prepare(t);
+}
+
+String::String(const char *s, Type t)
+{
+  d = new StringPrivate;
+
+  if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
+    debug("String::String() -- A const char * should not contain UTF16.");
+    return;
+  }
+
+  int length = ::strlen(s);
+  d->data.resize(length);
+
+  wstring::iterator targetIt = d->data.begin();
+
+  for(int i = 0; i < length; i++) {
+    *targetIt = uchar(s[i]);
+    ++targetIt;
+  }
+
+  prepare(t);
+}
+
+String::String(wchar_t c, Type t)
+{
+  d = new StringPrivate;
+  d->data += c;
+  prepare(t);
+}
+
+String::String(char c, Type t)
+{
+  d = new StringPrivate;
+
+  if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
+    debug("String::String() -- A std::string should not contain UTF16.");
+    return;
+  }
+
+  d->data += uchar(c);
+  prepare(t);
+}
+
+String::String(const ByteVector &v, Type t)
+{
+  d = new StringPrivate;
+
+  if(v.isEmpty())
+    return;
+
+  if(t == Latin1 || t == UTF8) {
+
+    int length = 0;
+    d->data.resize(v.size());
+    wstring::iterator targetIt = d->data.begin();
+    for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
+      *targetIt = uchar(*it);
+      ++targetIt;
+      ++length;
+    }
+    d->data.resize(length);
+  }
+  else  {
+    d->data.resize(v.size() / 2);
+    wstring::iterator targetIt = d->data.begin();
+
+    for(ByteVector::ConstIterator it = v.begin();
+        it != v.end() && it + 1 != v.end() && combine(*it, *(it + 1));
+        it += 2)
+    {
+      *targetIt = combine(*it, *(it + 1));
+      ++targetIt;
+    }
+  }
+  prepare(t);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+String::~String()
+{
+  if(d->deref())
+    delete d;
+}
+
+std::string String::to8Bit(bool unicode) const
+{
+  std::string s;
+  s.resize(d->data.size());
+
+  if(!unicode) {
+    std::string::iterator targetIt = s.begin();
+    for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+      *targetIt = char(*it);
+      ++targetIt;
+    }
+    return s;
+  }
+
+  const int outputBufferSize = d->data.size() * 3 + 1;
+
+  Unicode::UTF16 *sourceBuffer = new Unicode::UTF16[d->data.size() + 1];
+  Unicode::UTF8  *targetBuffer = new Unicode::UTF8[outputBufferSize];
+
+  for(unsigned int i = 0; i < d->data.size(); i++)
+    sourceBuffer[i] = Unicode::UTF16(d->data[i]);
+
+  const Unicode::UTF16 *source = sourceBuffer;
+  Unicode::UTF8 *target = targetBuffer;
+
+  Unicode::ConversionResult result =
+    Unicode::ConvertUTF16toUTF8(&source, sourceBuffer + d->data.size(),
+                                &target, targetBuffer + outputBufferSize,
+                                Unicode::lenientConversion);
+
+  if(result != Unicode::conversionOK)
+    debug("String::to8Bit() - Unicode conversion error.");
+
+  int newSize = target - targetBuffer;
+  s.resize(newSize);
+  targetBuffer[newSize] = 0;
+
+  s = (char *) targetBuffer;
+
+  delete [] sourceBuffer;
+  delete [] targetBuffer;
+
+  return s;
+}
+
+TagLib::wstring String::toWString() const
+{
+  return d->data;
+}
+
+const char *String::toCString(bool unicode) const
+{
+  delete [] d->CString;
+
+  std::string buffer = to8Bit(unicode);
+  d->CString = new char[buffer.size() + 1];
+  strcpy(d->CString, buffer.c_str());
+
+  return d->CString;
+}
+
+String::Iterator String::begin()
+{
+  return d->data.begin();
+}
+
+String::ConstIterator String::begin() const
+{
+  return d->data.begin();
+}
+
+String::Iterator String::end()
+{
+  return d->data.end();
+}
+
+String::ConstIterator String::end() const
+{
+  return d->data.end();
+}
+
+int String::find(const String &s, int offset) const
+{
+  wstring::size_type position = d->data.find(s.d->data, offset);
+
+  if(position != wstring::npos)
+    return position;
+  else
+    return -1;
+}
+
+int String::rfind(const String &s, int offset) const
+{
+  wstring::size_type position =
+    d->data.rfind(s.d->data, offset == -1 ? wstring::npos : offset);
+
+  if(position != wstring::npos)
+    return position;
+  else
+    return -1;
+}
+
+bool String::startsWith(const String &s) const
+{
+  if(s.length() > length())
+    return false;
+
+  return substr(0, s.length()) == s;
+}
+
+String String::substr(uint position, uint n) const
+{
+  if(n > position + d->data.size())
+    n = d->data.size() - position;
+
+  String s;
+  s.d->data = d->data.substr(position, n);
+  return s;
+}
+
+String &String::append(const String &s)
+{
+  detach();
+  d->data += s.d->data;
+  return *this;
+}
+
+String String::upper() const
+{
+  String s;
+
+  static int shift = 'A' - 'a';
+
+  for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) {
+    if(*it >= 'a' && *it <= 'z')
+      s.d->data.push_back(*it + shift);
+    else
+      s.d->data.push_back(*it);
+  }
+
+  return s;
+}
+
+TagLib::uint String::size() const
+{
+  return d->data.size();
+}
+
+TagLib::uint String::length() const
+{
+  return size();
+}
+
+bool String::isEmpty() const
+{
+  return d->data.size() == 0;
+}
+
+bool String::isNull() const
+{
+  return d == null.d;
+}
+
+ByteVector String::data(Type t) const
+{
+  ByteVector v;
+
+  switch(t) {
+
+  case Latin1:
+  {
+    for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+      v.append(char(*it));
+    break;
+  }
+  case UTF8:
+  {
+    std::string s = to8Bit(true);
+    v.setData(s.c_str(), s.length());
+    break;
+  }
+  case UTF16:
+  {
+    // Assume that if we're doing UTF16 and not UTF16BE that we want little
+    // endian encoding.  (Byte Order Mark)
+
+    v.append(char(0xff));
+    v.append(char(0xfe));
+
+    for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+
+      char c1 = *it & 0xff;
+      char c2 = *it >> 8;
+
+      v.append(c1);
+      v.append(c2);
+    }
+    break;
+  }
+  case UTF16BE:
+  {
+    for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+
+      char c1 = *it >> 8;
+      char c2 = *it & 0xff;
+
+      v.append(c1);
+      v.append(c2);
+    }
+    break;
+  }
+  case UTF16LE:
+  {
+    for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+
+      char c1 = *it & 0xff;
+      char c2 = *it >> 8;
+
+      v.append(c1);
+      v.append(c2);
+    }
+    break;
+  }
+  }
+
+  return v;
+}
+
+int String::toInt() const
+{
+  return toInt(0);
+}
+
+int String::toInt(bool *ok) const
+{
+  int value = 0;
+
+  uint size = d->data.size();
+  bool negative = size > 0 && d->data[0] == '-';
+  uint start = negative ? 1 : 0;
+  uint i = start;
+
+  for(; i < size && d->data[i] >= '0' && d->data[i] <= '9'; i++)
+    value = value * 10 + (d->data[i] - '0');
+
+  if(negative)
+    value = value * -1;
+
+  if(ok)
+    *ok = (size > start && i == size);
+
+  return value;
+}
+
+String String::stripWhiteSpace() const
+{
+  wstring::const_iterator begin = d->data.begin();
+  wstring::const_iterator end = d->data.end();
+
+  while(begin != end &&
+        (*begin == '\t' || *begin == '\n' || *begin == '\f' ||
+         *begin == '\r' || *begin == ' '))
+  {
+    ++begin;
+  }
+
+  if(begin == end)
+    return null;
+
+  // There must be at least one non-whitespace character here for us to have
+  // gotten this far, so we should be safe not doing bounds checking.
+
+  do {
+    --end;
+  } while(*end == '\t' || *end == '\n' ||
+          *end == '\f' || *end == '\r' || *end == ' ');
+
+  return String(wstring(begin, end + 1));
+}
+
+bool String::isLatin1() const
+{
+  for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+    if(*it >= 256)
+      return false;
+  }
+  return true;
+}
+
+bool String::isAscii() const
+{
+  for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+    if(*it >= 128)
+      return false;
+  }
+  return true;
+}
+
+String String::number(int n) // static
+{
+  if(n == 0)
+    return String("0");
+
+  String charStack;
+
+  bool negative = n < 0;
+
+  if(negative)
+    n = n * -1;
+
+  while(n > 0) {
+    int remainder = n % 10;
+    charStack += char(remainder + '0');
+    n = (n - remainder) / 10;
+  }
+
+  String s;
+
+  if(negative)
+    s += '-';
+
+  for(int i = charStack.d->data.size() - 1; i >= 0; i--)
+    s += charStack.d->data[i];
+
+  return s;
+}
+
+TagLib::wchar &String::operator[](int i)
+{
+  detach();
+
+  return d->data[i];
+}
+
+const TagLib::wchar &String::operator[](int i) const
+{
+  return d->data[i];
+}
+
+bool String::operator==(const String &s) const
+{
+  return d == s.d || d->data == s.d->data;
+}
+
+String &String::operator+=(const String &s)
+{
+  detach();
+
+  d->data += s.d->data;
+  return *this;
+}
+
+String &String::operator+=(const wchar_t *s)
+{
+  detach();
+
+  d->data += s;
+  return *this;
+}
+
+String &String::operator+=(const char *s)
+{
+  detach();
+
+  for(int i = 0; s[i] != 0; i++)
+    d->data += uchar(s[i]);
+  return *this;
+}
+
+String &String::operator+=(wchar_t c)
+{
+  detach();
+
+  d->data += c;
+  return *this;
+}
+
+String &String::operator+=(char c)
+{
+  detach();
+
+  d->data += uchar(c);
+  return *this;
+}
+
+String &String::operator=(const String &s)
+{
+  if(&s == this)
+    return *this;
+
+  if(d->deref())
+    delete d;
+  d = s.d;
+  d->ref();
+  return *this;
+}
+
+String &String::operator=(const std::string &s)
+{
+  if(d->deref())
+    delete d;
+
+  d = new StringPrivate;
+
+  d->data.resize(s.size());
+
+  wstring::iterator targetIt = d->data.begin();
+  for(std::string::const_iterator it = s.begin(); it != s.end(); it++) {
+    *targetIt = uchar(*it);
+    ++targetIt;
+  }
+
+  return *this;
+}
+
+String &String::operator=(const wstring &s)
+{
+  if(d->deref())
+    delete d;
+  d = new StringPrivate(s);
+  return *this;
+}
+
+String &String::operator=(const wchar_t *s)
+{
+  if(d->deref())
+    delete d;
+  d = new StringPrivate(s);
+  return *this;
+}
+
+String &String::operator=(char c)
+{
+  if(d->deref())
+    delete d;
+  d = new StringPrivate;
+  d->data += uchar(c);
+  return *this;
+}
+
+String &String::operator=(wchar_t c)
+{
+  if(d->deref())
+    delete d;
+  d = new StringPrivate;
+  d->data += c;
+  return *this;
+}
+
+String &String::operator=(const char *s)
+{
+  if(d->deref())
+    delete d;
+
+  d = new StringPrivate;
+
+  int length = ::strlen(s);
+  d->data.resize(length);
+
+  wstring::iterator targetIt = d->data.begin();
+  for(int i = 0; i < length; i++) {
+    *targetIt = uchar(s[i]);
+    ++targetIt;
+  }
+
+  return *this;
+}
+
+String &String::operator=(const ByteVector &v)
+{
+  if(d->deref())
+    delete d;
+
+  d = new StringPrivate;
+  d->data.resize(v.size());
+  wstring::iterator targetIt = d->data.begin();
+
+  uint i = 0;
+
+  for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
+    *targetIt = uchar(*it);
+    ++targetIt;
+    ++i;
+  }
+
+  // If we hit a null in the ByteVector, shrink the string again.
+
+  d->data.resize(i);
+
+  return *this;
+}
+
+bool String::operator<(const String &s) const
+{
+  return d->data < s.d->data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void String::detach()
+{
+  if(d->count() > 1) {
+    d->deref();
+    d = new StringPrivate(d->data);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void String::prepare(Type t)
+{
+  switch(t) {
+  case UTF16:
+  {
+    if(d->data.size() >= 1 && (d->data[0] == 0xfeff || d->data[0] == 0xfffe)) {
+      bool swap = d->data[0] != 0xfeff;
+      d->data.erase(d->data.begin(), d->data.begin() + 1);
+      if(swap) {
+        for(uint i = 0; i < d->data.size(); i++)
+          d->data[i] = byteSwap((unsigned short)d->data[i]);
+      }
+    }
+    else {
+      debug("String::prepare() - Invalid UTF16 string.");
+      d->data.erase(d->data.begin(), d->data.end());
+    }
+    break;
+  }
+  case UTF8:
+  {
+    int bufferSize = d->data.size() + 1;
+    Unicode::UTF8  *sourceBuffer = new Unicode::UTF8[bufferSize];
+    Unicode::UTF16 *targetBuffer = new Unicode::UTF16[bufferSize];
+
+    unsigned int i = 0;
+    for(; i < d->data.size(); i++)
+      sourceBuffer[i] = Unicode::UTF8(d->data[i]);
+    sourceBuffer[i] = 0;
+
+    const Unicode::UTF8 *source = sourceBuffer;
+    Unicode::UTF16 *target = targetBuffer;
+
+    Unicode::ConversionResult result =
+      Unicode::ConvertUTF8toUTF16(&source, sourceBuffer + bufferSize,
+                                  &target, targetBuffer + bufferSize,
+                                  Unicode::lenientConversion);
+
+    if(result != Unicode::conversionOK)
+      debug("String::prepare() - Unicode conversion error.");
+
+
+    int newSize = target != targetBuffer ? target - targetBuffer - 1 : 0;
+    d->data.resize(newSize);
+
+    for(int i = 0; i < newSize; i++)
+      d->data[i] = targetBuffer[i];
+
+    delete [] sourceBuffer;
+    delete [] targetBuffer;
+
+    break;
+  }
+  case UTF16LE:
+  {
+    for(uint i = 0; i < d->data.size(); i++)
+      d->data[i] = byteSwap((unsigned short)d->data[i]);
+    break;
+  }
+  default:
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2)
+{
+  String s(s1);
+  s.append(s2);
+  return s;
+}
+
+const TagLib::String operator+(const char *s1, const TagLib::String &s2)
+{
+  String s(s1);
+  s.append(s2);
+  return s;
+}
+
+const TagLib::String operator+(const TagLib::String &s1, const char *s2)
+{
+  String s(s1);
+  s.append(s2);
+  return s;
+}
+
+std::ostream &operator<<(std::ostream &s, const String &str)
+{
+  s << str.to8Bit();
+  return s;
+}
diff --git a/src/taglib/toolkit/tstring.h b/src/taglib/toolkit/tstring.h
new file mode 100644 (file)
index 0000000..eb7a01e
--- /dev/null
@@ -0,0 +1,476 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_STRING_H
+#define TAGLIB_STRING_H
+
+#include "taglib_export.h"
+#include "taglib.h"
+#include "tbytevector.h"
+
+#include <string>
+#include <iostream>
+
+/*!
+ * \relates TagLib::String
+ *
+ * Converts a TagLib::String to a QString without a requirement to link to Qt.
+ */
+#define QStringToTString(s) TagLib::String(s.utf8().data(), TagLib::String::UTF8)
+
+/*!
+ * \relates TagLib::String
+ *
+ * Converts a TagLib::String to a QString without a requirement to link to Qt.
+ */
+#define TStringToQString(s) QString::fromUtf8(s.toCString(true))
+
+namespace TagLib {
+
+  //! A \e wide string class suitable for unicode.
+
+  /*!
+   * This is an implicitly shared \e wide string.  For storage it uses
+   * TagLib::wstring, but as this is an <i>implementation detail</i> this of
+   * course could change.  Strings are stored internally as UTF-16BE.  (Without
+   * the BOM (Byte Order Mark)
+   *
+   * The use of implicit sharing means that copying a string is cheap, the only
+   * \e cost comes into play when the copy is modified.  Prior to that the string
+   * just has a pointer to the data of the \e parent String.  This also makes
+   * this class suitable as a function return type.
+   *
+   * In addition to adding implicit sharing, this class keeps track of four
+   * possible encodings, which are the four supported by the ID3v2 standard.
+   */
+
+  class TAGLIB_EXPORT String
+  {
+  public:
+
+#ifndef DO_NOT_DOCUMENT
+    typedef std::basic_string<wchar>::iterator Iterator;
+    typedef std::basic_string<wchar>::const_iterator ConstIterator;
+#endif
+
+    /**
+     * The four types of string encodings supported by the ID3v2 specification.
+     * ID3v1 is assumed to be Latin1 and Ogg Vorbis comments use UTF8.
+     */
+    enum Type {
+      /*!
+       * IS08859-1, or <i>Latin1</i> encoding.  8 bit characters.
+       */
+      Latin1 = 0,
+      /*!
+       * UTF16 with a <i>byte order mark</i>.  16 bit characters.
+       */
+      UTF16 = 1,
+      /*!
+       * UTF16 <i>big endian</i>.  16 bit characters.  This is the encoding used
+       * internally by TagLib.
+       */
+      UTF16BE = 2,
+      /*!
+       * UTF8 encoding.  Characters are usually 8 bits but can be up to 32.
+       */
+      UTF8 = 3,
+      /*!
+       * UTF16 <i>little endian</i>.  16 bit characters.
+       */
+      UTF16LE = 4
+    };
+
+    /*!
+     * Constructs an empty String.
+     */
+    String();
+
+    /*!
+     * Make a shallow, implicitly shared, copy of \a s.  Because this is
+     * implicitly shared, this method is lightweight and suitable for
+     * pass-by-value usage.
+     */
+    String(const String &s);
+
+    /*!
+     * Makes a deep copy of the data in \a s.
+     *
+     * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+     * used with other codecs it will simply print a warning and exit.
+     */
+    String(const std::string &s, Type t = Latin1);
+
+    /*!
+     * Makes a deep copy of the data in \a s.
+     */
+    String(const wstring &s, Type t = UTF16BE);
+
+    /*!
+     * Makes a deep copy of the data in \a s.
+     */
+    String(const wchar_t *s, Type t = UTF16BE);
+
+    /*!
+     * Makes a deep copy of the data in \a c.
+     *
+     * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+     * used with other codecs it will simply print a warning and exit.
+     */
+    String(char c, Type t = Latin1);
+
+    /*!
+     * Makes a deep copy of the data in \a c.
+     */
+    String(wchar_t c, Type t = Latin1);
+
+
+    /*!
+     * Makes a deep copy of the data in \a s.
+     *
+     * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+     * used with other codecs it will simply print a warning and exit.
+     */
+    String(const char *s, Type t = Latin1);
+
+    /*!
+     * Makes a deep copy of the data in \a s.
+     *
+     * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+     * used with other codecs it will simply print a warning and exit.
+     */
+    String(const ByteVector &v, Type t = Latin1);
+
+    /*!
+     * Destroys this String instance.
+     */
+    virtual ~String();
+
+    /*!
+     * If \a unicode if false (the default) this will return a \e Latin1 encoded
+     * std::string.  If it is true the returned std::wstring will be UTF-8
+     * encoded.
+     */
+    std::string to8Bit(bool unicode = false) const;
+
+    /*!
+     * Returns a wstring version of the TagLib string as a wide string.
+     */
+    wstring toWString() const;
+
+    /*!
+     * Creates and returns a C-String based on the data.  This string is still
+     * owned by the String (class) and as such should not be deleted by the user.
+     *
+     * If \a unicode if false (the default) this string will be encoded in
+     * \e Latin1.  If it is true the returned C-String will be UTF-8 encoded.
+     *
+     * This string remains valid until the String instance is destroyed or
+     * another export method is called.
+     *
+     * \warning This however has the side effect that this C-String will remain
+     * in memory <b>in addition to</b> other memory that is consumed by the
+     * String instance.  So, this method should not be used on large strings or
+     * where memory is critical.
+     */
+    const char *toCString(bool unicode = false) const;
+
+    /*!
+     * Returns an iterator pointing to the beginning of the string.
+     */
+    Iterator begin();
+
+    /*!
+     * Returns a const iterator pointing to the beginning of the string.
+     */
+    ConstIterator begin() const;
+
+    /*!
+     * Returns an iterator pointing to the end of the string (the position
+     * after the last character).
+     */
+    Iterator end();
+
+    /*!
+     * Returns a const iterator pointing to the end of the string (the position
+     * after the last character).
+     */
+    ConstIterator end() const;
+
+    /*!
+     * Finds the first occurrence of pattern \a s in this string starting from
+     * \a offset.  If the pattern is not found, -1 is returned.
+     */
+    int find(const String &s, int offset = 0) const;
+
+    /*!
+     * Finds the last occurrence of pattern \a s in this string, searched backwards,
+     * either from the end of the string or starting from \a offset. If the pattern
+     * is not found, -1 is returned.
+     */
+    int rfind(const String &s, int offset = -1) const;
+
+    /*!
+     * Returns true if the strings starts with the substring \a s.
+     */
+    bool startsWith(const String &s) const;
+
+    /*!
+     * Extract a substring from this string starting at \a position and
+     * continuing for \a n characters.
+     */
+    String substr(uint position, uint n = 0xffffffff) const;
+
+    /*!
+     * Append \a s to the current string and return a reference to the current
+     * string.
+     */
+    String &append(const String &s);
+
+    /*!
+     * Returns an upper case version of the string.
+     *
+     * \warning This only works for the characters in US-ASCII, i.e. A-Z.
+     */
+    String upper() const;
+
+    /*!
+     * Returns the size of the string.
+     */
+    uint size() const;
+
+    /*!
+     * Returns the length of the string.  Equivalent to size().
+     */
+    uint length() const;
+
+    /*!
+     * Returns true if the string is empty.
+     *
+     * \see isNull()
+     */
+    bool isEmpty() const;
+
+    /*!
+     * Returns true if this string is null -- i.e. it is a copy of the
+     * String::null string.
+     *
+     * \note A string can be empty and not null.
+     * \see isEmpty()
+     */
+    bool isNull() const;
+
+    /*!
+     * Returns a ByteVector containing the string's data.  If \a t is Latin1 or
+     * UTF8, this will return a vector of 8 bit characters, otherwise it will use
+     * 16 bit characters.
+     */
+    ByteVector data(Type t) const;
+
+    /*!
+     * Convert the string to an integer.
+     *
+     * Returns the integer if the conversion was successfull or 0 if the
+     * string does not represent a number.
+     */
+    // BIC: merge with the method below
+    int toInt() const;
+
+    /*!
+     * Convert the string to an integer.
+     *
+     * If the conversion was successfull, it sets the value of \a *ok to
+     * true and returns the integer. Otherwise it sets \a *ok to false
+     * and the result is undefined.
+     */
+    int toInt(bool *ok) const;
+
+    /*!
+     * Returns a string with the leading and trailing whitespace stripped.
+     */
+    String stripWhiteSpace() const;
+
+    /*!
+     * Returns true if the file only uses characters required by Latin1.
+     */
+    bool isLatin1() const;
+
+    /*!
+     * Returns true if the file only uses characters required by (7-bit) ASCII.
+     */
+    bool isAscii() const;
+
+    /*!
+     * Converts the base-10 integer \a n to a string.
+     */
+    static String number(int n);
+
+    /*!
+     * Returns a reference to the character at position \a i.
+     */
+    wchar &operator[](int i);
+
+    /*!
+     * Returns a const reference to the character at position \a i.
+     */
+    const wchar &operator[](int i) const;
+
+    /*!
+     * Compares each character of the String with each character of \a s and
+     * returns true if the strings match.
+     */
+    bool operator==(const String &s) const;
+
+    /*!
+     * Appends \a s to the end of the String.
+     */
+    String &operator+=(const String &s);
+
+    /*!
+     * Appends \a s to the end of the String.
+     */
+    String &operator+=(const wchar_t* s);
+
+    /*!
+     * Appends \a s to the end of the String.
+     */
+    String &operator+=(const char* s);
+
+    /*!
+     * Appends \a s to the end of the String.
+     */
+    String &operator+=(wchar_t c);
+
+    /*!
+     * Appends \a c to the end of the String.
+     */
+    String &operator+=(char c);
+
+    /*!
+     * Performs a shallow, implicitly shared, copy of \a s, overwriting the
+     * String's current data.
+     */
+    String &operator=(const String &s);
+
+    /*!
+     * Performs a deep copy of the data in \a s.
+     */
+    String &operator=(const std::string &s);
+
+    /*!
+     * Performs a deep copy of the data in \a s.
+     */
+    String &operator=(const wstring &s);
+
+    /*!
+     * Performs a deep copy of the data in \a s.
+     */
+    String &operator=(const wchar_t *s);
+
+    /*!
+     * Performs a deep copy of the data in \a s.
+     */
+    String &operator=(char c);
+
+    /*!
+     * Performs a deep copy of the data in \a s.
+     */
+    String &operator=(wchar_t c);
+
+    /*!
+     * Performs a deep copy of the data in \a s.
+     */
+    String &operator=(const char *s);
+
+    /*!
+     * Performs a deep copy of the data in \a v.
+     */
+    String &operator=(const ByteVector &v);
+
+    /*!
+     * To be able to use this class in a Map, this operator needed to be
+     * implemented.  Returns true if \a s is less than this string in a bytewise
+     * comparison.
+     */
+    bool operator<(const String &s) const;
+
+    /*!
+     * A null string provided for convenience.
+     */
+    static String null;
+
+  protected:
+    /*!
+     * If this String is being shared via implicit sharing, do a deep copy of the
+     * data and separate from the shared members.  This should be called by all
+     * non-const subclass members.
+     */
+    void detach();
+
+  private:
+    /*!
+     * This checks to see if the string is in \e UTF-16 (with BOM) or \e UTF-8
+     * format and if so converts it to \e UTF-16BE for internal use.  \e Latin1
+     * does not require conversion since it is a subset of \e UTF-16BE and
+     * \e UTF16-BE requires no conversion since it is used internally.
+     */
+    void prepare(Type t);
+
+    class StringPrivate;
+    StringPrivate *d;
+  };
+
+}
+
+/*!
+ * \relates TagLib::String
+ *
+ * Concatenates \a s1 and \a s2 and returns the result as a string.
+ */
+TAGLIB_EXPORT const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2);
+
+/*!
+ * \relates TagLib::String
+ *
+ * Concatenates \a s1 and \a s2 and returns the result as a string.
+ */
+TAGLIB_EXPORT const TagLib::String operator+(const char *s1, const TagLib::String &s2);
+
+/*!
+ * \relates TagLib::String
+ *
+ * Concatenates \a s1 and \a s2 and returns the result as a string.
+ */
+TAGLIB_EXPORT const TagLib::String operator+(const TagLib::String &s1, const char *s2);
+
+
+/*!
+ * \relates TagLib::String
+ *
+ * Send the string to an output stream.
+ */
+TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const TagLib::String &str);
+
+#endif
diff --git a/src/taglib/toolkit/tstringlist.cpp b/src/taglib/toolkit/tstringlist.cpp
new file mode 100644 (file)
index 0000000..1da3a29
--- /dev/null
@@ -0,0 +1,123 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "tstringlist.h"
+
+using namespace TagLib;
+
+class StringListPrivate
+{
+
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+StringList StringList::split(const String &s, const String &pattern)
+{
+  StringList l;
+
+  int previousOffset = 0;
+  for(int offset = s.find(pattern); offset != -1; offset = s.find(pattern, offset + 1)) {
+    l.append(s.substr(previousOffset, offset - previousOffset));
+    previousOffset = offset + 1;
+  }
+
+  l.append(s.substr(previousOffset, s.size() - previousOffset));
+
+  return l;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+StringList::StringList() : List<String>()
+{
+
+}
+
+StringList::StringList(const StringList &l) : List<String>(l)
+{
+
+}
+
+StringList::StringList(const String &s) : List<String>()
+{
+  append(s);
+}
+
+StringList::StringList(const ByteVectorList &bl, String::Type t) : List<String>()
+{
+  ByteVectorList::ConstIterator i = bl.begin();
+  for(;i != bl.end(); i++) {
+    append(String(*i, t));
+  }
+}
+
+StringList::~StringList()
+{
+
+}
+
+String StringList::toString(const String &separator) const
+{
+  String s;
+
+  ConstIterator it = begin();
+  ConstIterator itEnd = end();
+
+  while(it != itEnd) {
+    s += *it;
+    it++;
+    if(it != itEnd)
+      s += separator;
+  }
+
+  return s;
+}
+
+StringList &StringList::append(const String &s)
+{
+  List<String>::append(s);
+  return *this;
+}
+
+StringList &StringList::append(const StringList &l)
+{
+  List<String>::append(l);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+std::ostream &operator<<(std::ostream &s, const StringList &l)
+{
+  s << l.toString();
+  return s;
+}
diff --git a/src/taglib/toolkit/tstringlist.h b/src/taglib/toolkit/tstringlist.h
new file mode 100644 (file)
index 0000000..7bb8664
--- /dev/null
@@ -0,0 +1,115 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 by Scott Wheeler
+    email                : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_STRINGLIST_H
+#define TAGLIB_STRINGLIST_H
+
+#include "tstring.h"
+#include "tlist.h"
+#include "tbytevectorlist.h"
+#include "taglib_export.h"
+
+#include <iostream>
+
+namespace TagLib {
+
+  //! A list of strings
+
+  /*!
+   * This is a spcialization of the List class with some members convention for
+   * string operations.
+   */
+
+  class TAGLIB_EXPORT StringList : public List<String>
+  {
+  public:
+
+    /*!
+     * Constructs an empty StringList.
+     */
+    StringList();
+
+    /*!
+     * Make a shallow, implicitly shared, copy of \a l.  Because this is
+     * implicitly shared, this method is lightweight and suitable for
+     * pass-by-value usage.
+     */
+    StringList(const StringList &l);
+
+    /*!
+     * Constructs a StringList with \a s as a member.
+     */
+    StringList(const String &s);
+
+    /*!
+     * Makes a deep copy of the data in \a vl.
+     *
+     * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+     * used with other codecs it will simply print a warning and exit.
+     */
+    StringList(const ByteVectorList &vl, String::Type t = String::Latin1);
+
+    /*!
+     * Destroys this StringList instance.
+     */
+    virtual ~StringList();
+
+    /*!
+     * Concatenate the list of strings into one string separated by \a separator.
+     */
+    String toString(const String &separator = " ") const;
+
+    /*!
+     * Appends \a s to the end of the list and returns a reference to the
+     * list.
+     */
+    StringList &append(const String &s);
+
+    /*!
+     * Appends all of the values in \a l to the end of the list and returns a
+     * reference to the list.
+     */
+    StringList &append(const StringList &l);
+
+    /*!
+     * Splits the String \a s into several strings at \a pattern.  This will not include
+     * the pattern in the returned strings.
+     */
+    static StringList split(const String &s, const String &pattern);
+
+  private:
+    class StringListPrivate;
+    StringListPrivate *d;
+  };
+
+}
+
+/*!
+ * \related TagLib::StringList
+ * Send the StringList to an output stream.
+ */
+std::ostream &operator<<(std::ostream &s, const TagLib::StringList &l);
+
+#endif
diff --git a/src/taglib/toolkit/unicode.cpp b/src/taglib/toolkit/unicode.cpp
new file mode 100644 (file)
index 0000000..b60264d
--- /dev/null
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ *                                                                             *
+ * THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB       *
+ * AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
+ * AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY.                       *
+ *                                                                             *
+ *******************************************************************************/
+
+/*
+ * Copyright 2001 Unicode, Inc.
+ * 
+ * Disclaimer
+ * 
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ * 
+ * Limitations on Rights to Redistribute This Code
+ * 
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
+ * the UTF32 conversion functions and to place the appropriate functions
+ * in their own C++ namespace.
+ */
+
+/* ---------------------------------------------------------------------
+
+    Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+       Author: Mark E. Davis, 1994.
+       Rev History: Rick McGowan, fixes & updates May 2001.
+       Sept 2001: fixed const & error conditions per
+               mods suggested by S. Parent & A. Lillich.
+
+    See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "unicode.h"
+#include <stdio.h>
+
+#define UNI_SUR_HIGH_START     (UTF32)0xD800
+#define UNI_SUR_HIGH_END       (UTF32)0xDBFF
+#define UNI_SUR_LOW_START      (UTF32)0xDC00
+#define UNI_SUR_LOW_END                (UTF32)0xDFFF
+#define false                  0
+#define true                   1
+
+namespace Unicode {
+
+static const int halfShift     = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase    = 0x0010000UL;
+static const UTF32 halfMask    = 0x3FFUL;
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ */
+static const char trailingBytesForUTF8[256] = {
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+       2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 
+                                        0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow.  There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... six byte sequence.)
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8 (
+               const UTF16** sourceStart, const UTF16* sourceEnd, 
+               UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+       ConversionResult result = conversionOK;
+       const UTF16* source = *sourceStart;
+       UTF8* target = *targetStart;
+       while (source < sourceEnd) {
+               UTF32 ch;
+               unsigned short bytesToWrite = 0;
+               const UTF32 byteMask = 0xBF;
+               const UTF32 byteMark = 0x80; 
+               const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+               ch = *source++;
+               /* If we have a surrogate pair, convert to UTF32 first. */
+               if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && source < sourceEnd) {
+                       UTF32 ch2 = *source;
+                       if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+                               ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+                                       + (ch2 - UNI_SUR_LOW_START) + halfBase;
+                               ++source;
+                       } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+                               --source; /* return to the illegal value itself */
+                               result = sourceIllegal;
+                               break;
+                       }
+               } else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) {
+                       --source; /* return to the illegal value itself */
+                       result = sourceIllegal;
+                       break;
+               }
+               /* Figure out how many bytes the result will require */
+               if (ch < (UTF32)0x80) {                 bytesToWrite = 1;
+               } else if (ch < (UTF32)0x800) {         bytesToWrite = 2;
+               } else if (ch < (UTF32)0x10000) {       bytesToWrite = 3;
+               } else if (ch < (UTF32)0x200000) {      bytesToWrite = 4;
+               } else {                                bytesToWrite = 2;
+                                                       ch = UNI_REPLACEMENT_CHAR;
+               }
+               // printf("bytes to write = %i\n", bytesToWrite);
+               target += bytesToWrite;
+               if (target > targetEnd) {
+                       source = oldSource; /* Back up source pointer! */
+                       target -= bytesToWrite; result = targetExhausted; break;
+               }
+               switch (bytesToWrite) { /* note: everything falls through. */
+                       case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+                       case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+                       case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+                       case 1: *--target =  ch | firstByteMark[bytesToWrite];
+               }
+               target += bytesToWrite;
+       }
+       *sourceStart = source;
+       *targetStart = target;
+       return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ *     length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false.  The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+       UTF8 a;
+       const UTF8 *srcptr = source+length;
+       switch (length) {
+       default: return false;
+               /* Everything else falls through when "true"... */
+       case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+       case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+       case 2: if ((a = (*--srcptr)) > 0xBF) return false;
+               switch (*source) {
+                   /* no fall-through in this inner switch */
+                   case 0xE0: if (a < 0xA0) return false; break;
+                   case 0xF0: if (a < 0x90) return false; break;
+                   case 0xF4: if (a > 0x8F) return false; break;
+                   default:  if (a < 0x80) return false;
+               }
+       case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+               if (*source > 0xF4) return false;
+       }
+       return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+       int length = trailingBytesForUTF8[*source]+1;
+       if (source+length > sourceEnd) {
+           return false;
+       }
+       return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+               const UTF8** sourceStart, const UTF8* sourceEnd, 
+               UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+       ConversionResult result = conversionOK;
+       const UTF8* source = *sourceStart;
+       UTF16* target = *targetStart;
+       while (source < sourceEnd) {
+               UTF32 ch = 0;
+               unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+               if (source + extraBytesToRead >= sourceEnd) {
+                       result = sourceExhausted; break;
+               }
+               /* Do this check whether lenient or strict */
+               if (! isLegalUTF8(source, extraBytesToRead+1)) {
+                       result = sourceIllegal;
+                       break;
+               }
+               /*
+                * The cases all fall through. See "Note A" below.
+                */
+               switch (extraBytesToRead) {
+                       case 3: ch += *source++; ch <<= 6;
+                       case 2: ch += *source++; ch <<= 6;
+                       case 1: ch += *source++; ch <<= 6;
+                       case 0: ch += *source++;
+               }
+               ch -= offsetsFromUTF8[extraBytesToRead];
+
+               if (target >= targetEnd) {
+                       source -= (extraBytesToRead+1); /* Back up source pointer! */
+                       result = targetExhausted; break;
+               }
+               if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+                       if ((flags == strictConversion) && (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)) {
+                               source -= (extraBytesToRead+1); /* return to the illegal value itself */
+                               result = sourceIllegal;
+                               break;
+                       } else {
+                           *target++ = ch;     /* normal case */
+                       }
+               } else if (ch > UNI_MAX_UTF16) {
+                       if (flags == strictConversion) {
+                               result = sourceIllegal;
+                               source -= (extraBytesToRead+1); /* return to the start */
+                               break; /* Bail out; shouldn't continue */
+                       } else {
+                               *target++ = UNI_REPLACEMENT_CHAR;
+                       }
+               } else {
+                       /* target is a character in range 0xFFFF - 0x10FFFF. */
+                       if (target + 1 >= targetEnd) {
+                               source -= (extraBytesToRead+1); /* Back up source pointer! */
+                               result = targetExhausted; break;
+                       }
+                       ch -= halfBase;
+                       *target++ = (ch >> halfShift) + UNI_SUR_HIGH_START;
+                       *target++ = (ch & halfMask) + UNI_SUR_LOW_START;
+               }
+       }
+       *sourceStart = source;
+       *targetStart = target;
+       return result;
+}
+
+}
+
+/* ---------------------------------------------------------------------
+
+       Note A.
+       The fall-through switches in UTF-8 reading code save a
+       temp variable, some decrements & conditionals.  The switches
+       are equivalent to the following loop:
+               {
+                       int tmpBytesToRead = extraBytesToRead+1;
+                       do {
+                               ch += *source++;
+                               --tmpBytesToRead;
+                               if (tmpBytesToRead) ch <<= 6;
+                       } while (tmpBytesToRead > 0);
+               }
+       In UTF-8 writing code, the switches on "bytesToWrite" are
+       similarly unrolled loops.
+
+   --------------------------------------------------------------------- */
+
+
diff --git a/src/taglib/toolkit/unicode.h b/src/taglib/toolkit/unicode.h
new file mode 100644 (file)
index 0000000..cf7eb3c
--- /dev/null
@@ -0,0 +1,149 @@
+#ifndef TAGLIB_UNICODE_H
+#define TAGLIB_UNICODE_H
+
+/*******************************************************************************
+ *                                                                             *
+ * THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB       *
+ * AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
+ * AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY.                       *
+ *                                                                             *
+ *******************************************************************************/
+
+#ifndef DO_NOT_DOCUMENT  // tell Doxygen not to document this header
+
+/*
+ * Copyright 2001 Unicode, Inc.
+ * 
+ * Disclaimer
+ * 
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ * 
+ * Limitations on Rights to Redistribute This Code
+ * 
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
+ * the UTF32 conversion functions and to place the appropriate functions
+ * in their own C++ namespace.
+ */
+
+/* ---------------------------------------------------------------------
+
+    Conversions between UTF32, UTF-16, and UTF-8.  Header file.
+
+    Several functions are included here, forming a complete set of
+    conversions between the three formats.  UTF-7 is not included
+    here, but is handled in a separate source file.
+
+    Each of these routines takes pointers to input buffers and output
+    buffers.  The input buffers are const.
+
+    Each routine converts the text between *sourceStart and sourceEnd,
+    putting the result into the buffer between *targetStart and
+    targetEnd. Note: the end pointers are *after* the last item: e.g. 
+    *(sourceEnd - 1) is the last item.
+
+    The return result indicates whether the conversion was successful,
+    and if not, whether the problem was in the source or target buffers.
+    (Only the first encountered problem is indicated.)
+
+    After the conversion, *sourceStart and *targetStart are both
+    updated to point to the end of last text successfully converted in
+    the respective buffers.
+
+    Input parameters:
+       sourceStart - pointer to a pointer to the source buffer.
+               The contents of this are modified on return so that
+               it points at the next thing to be converted.
+       targetStart - similarly, pointer to pointer to the target buffer.
+       sourceEnd, targetEnd - respectively pointers to the ends of the
+               two buffers, for overflow checking only.
+
+    These conversion functions take a ConversionFlags argument. When this
+    flag is set to strict, both irregular sequences and isolated surrogates
+    will cause an error.  When the flag is set to lenient, both irregular
+    sequences and isolated surrogates are converted.
+
+    Whether the flag is strict or lenient, all illegal sequences will cause
+    an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
+    or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
+    must check for illegal sequences.
+
+    When the flag is set to lenient, characters over 0x10FFFF are converted
+    to the replacement character; otherwise (when the flag is set to strict)
+    they constitute an error.
+
+    Output parameters:
+       The value "sourceIllegal" is returned from some routines if the input
+       sequence is malformed.  When "sourceIllegal" is returned, the source
+       value will point to the illegal value that caused the problem. E.g.,
+       in UTF-8 when a sequence is malformed, it points to the start of the
+       malformed sequence.  
+
+    Author: Mark E. Davis, 1994.
+    Rev History: Rick McGowan, fixes & updates May 2001.
+                Fixes & updates, Sept 2001.
+
+------------------------------------------------------------------------ */
+
+/* ---------------------------------------------------------------------
+    The following 4 definitions are compiler-specific.
+    The C standard does not guarantee that wchar_t has at least
+    16 bits, so wchar_t is no less portable than unsigned short!
+    All should be unsigned values to avoid sign extension during
+    bit mask & shift operations.
+------------------------------------------------------------------------ */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+
+namespace Unicode {
+
+typedef unsigned long  UTF32;  /* at least 32 bits */
+typedef unsigned short UTF16;  /* at least 16 bits */
+typedef unsigned char  UTF8;   /* typically 8 bits */
+typedef unsigned char  Boolean; /* 0 or 1 */
+
+typedef enum {
+       conversionOK = 0,       /* conversion successful */
+       sourceExhausted = 1,    /* partial character in source, but hit end */
+       targetExhausted = 2,    /* insuff. room in target for conversion */
+       sourceIllegal = 3       /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum {
+       strictConversion = 0,
+       lenientConversion
+} ConversionFlags;
+
+ConversionResult ConvertUTF8toUTF16 (
+               const UTF8** sourceStart, const UTF8* sourceEnd, 
+               UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF8 (
+               const UTF16** sourceStart, const UTF16* sourceEnd, 
+               UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+               
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
+
+} // namespace Unicode
+
+/* --------------------------------------------------------------------- */
+
+#endif
+#endif
diff --git a/src/taglib/trueaudio/trueaudiofile.cpp b/src/taglib/trueaudio/trueaudiofile.cpp
new file mode 100644 (file)
index 0000000..2a0ccaa
--- /dev/null
@@ -0,0 +1,274 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tdebug.h>
+#include <tagunion.h>
+
+#include "trueaudiofile.h"
+#include "id3v1tag.h"
+#include "id3v2tag.h"
+#include "id3v2header.h"
+
+using namespace TagLib;
+
+namespace
+{
+  enum { ID3v2Index = 0, ID3v1Index = 1 };
+}
+
+class TrueAudio::File::FilePrivate
+{
+public:
+  FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
+    ID3v2FrameFactory(frameFactory),
+    ID3v2Location(-1),
+    ID3v2OriginalSize(0),
+    ID3v1Location(-1),
+    properties(0),
+    scanned(false),
+    hasID3v1(false),
+    hasID3v2(false) {}
+
+  ~FilePrivate()
+  {
+    delete properties;
+  }
+
+  const ID3v2::FrameFactory *ID3v2FrameFactory;
+  long ID3v2Location;
+  uint ID3v2OriginalSize;
+
+  long ID3v1Location;
+
+  TagUnion tag;
+
+  Properties *properties;
+  bool scanned;
+
+  // These indicate whether the file *on disk* has these tags, not if
+  // this data structure does.  This is used in computing offsets.
+
+  bool hasID3v1;
+  bool hasID3v2;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+TrueAudio::File::File(FileName file, bool readProperties,
+                 Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+  d = new FilePrivate;
+  if(isOpen())
+    read(readProperties, propertiesStyle);
+}
+
+TrueAudio::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
+                 bool readProperties, Properties::ReadStyle propertiesStyle) :
+  TagLib::File(file)
+{
+  d = new FilePrivate(frameFactory);
+  if(isOpen())
+    read(readProperties, propertiesStyle);
+}
+
+TrueAudio::File::~File()
+{
+  delete d;
+}
+
+TagLib::Tag *TrueAudio::File::tag() const
+{
+  return &d->tag;
+}
+
+TrueAudio::Properties *TrueAudio::File::audioProperties() const
+{
+  return d->properties;
+}
+
+void TrueAudio::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
+{
+  d->ID3v2FrameFactory = factory;
+}
+
+bool TrueAudio::File::save()
+{
+  if(readOnly()) {
+    debug("TrueAudio::File::save() -- File is read only.");
+    return false;
+  }
+
+  // Update ID3v2 tag
+
+  if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
+    if(!d->hasID3v2) {
+      d->ID3v2Location = 0;
+      d->ID3v2OriginalSize = 0;
+    }
+    ByteVector data = ID3v2Tag()->render();
+    insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
+    d->ID3v1Location -= d->ID3v2OriginalSize - data.size();
+    d->ID3v2OriginalSize = data.size();
+    d->hasID3v2 = true;
+  }
+  else if(d->hasID3v2) {
+    removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
+    d->ID3v1Location -= d->ID3v2OriginalSize;
+    d->ID3v2Location = -1;
+    d->ID3v2OriginalSize = 0;
+    d->hasID3v2 = false;
+  }
+
+  // Update ID3v1 tag
+
+  if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
+    if(!d->hasID3v1) {
+      seek(0, End);
+      d->ID3v1Location = tell();
+    }
+    else
+      seek(d->ID3v1Location);
+    writeBlock(ID3v1Tag()->render());
+    d->hasID3v1 = true;
+  }
+  else if(d->hasID3v1) {
+    removeBlock(d->ID3v1Location, 128);
+    d->ID3v1Location = -1;
+    d->hasID3v1 = false;
+  }
+
+  return true;
+}
+
+ID3v1::Tag *TrueAudio::File::ID3v1Tag(bool create)
+{
+  return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
+}
+
+ID3v2::Tag *TrueAudio::File::ID3v2Tag(bool create)
+{
+  return d->tag.access<ID3v2::Tag>(ID3v2Index, create);
+}
+
+void TrueAudio::File::strip(int tags)
+{
+  if(tags & ID3v1) {
+    d->tag.set(ID3v1Index, 0);
+    ID3v2Tag(true);
+  }
+
+  if(tags & ID3v2) {
+    d->tag.set(ID3v2Index, 0);
+
+    if(!ID3v1Tag())
+      ID3v2Tag(true);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void TrueAudio::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
+{
+  // Look for an ID3v2 tag
+
+  d->ID3v2Location = findID3v2();
+
+  if(d->ID3v2Location >= 0) {
+
+    d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
+
+    d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
+
+    if(ID3v2Tag()->header()->tagSize() <= 0)
+      d->tag.set(ID3v2Index, 0);
+    else
+      d->hasID3v2 = true;
+  }
+
+  // Look for an ID3v1 tag
+
+  d->ID3v1Location = findID3v1();
+
+  if(d->ID3v1Location >= 0) {
+    d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
+    d->hasID3v1 = true;
+  }
+
+  if(!d->hasID3v1)
+    ID3v2Tag(true);
+
+  // Look for TrueAudio metadata
+
+  if(readProperties) {
+    if(d->ID3v2Location >= 0) {
+      seek(d->ID3v2Location + d->ID3v2OriginalSize);
+      d->properties = new Properties(readBlock(TrueAudio::HeaderSize),
+                                     length() - d->ID3v2OriginalSize);
+    }
+    else {
+      seek(0);
+      d->properties = new Properties(readBlock(TrueAudio::HeaderSize),
+                                     length());
+    }
+  }
+}
+
+long TrueAudio::File::findID3v1()
+{
+  if(!isValid())
+    return -1;
+
+  seek(-128, End);
+  long p = tell();
+
+  if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+    return p;
+
+  return -1;
+}
+
+long TrueAudio::File::findID3v2()
+{
+  if(!isValid())
+    return -1;
+
+  seek(0);
+
+  if(readBlock(3) == ID3v2::Header::fileIdentifier())
+    return 0;
+
+  return -1;
+}
diff --git a/src/taglib/trueaudio/trueaudiofile.h b/src/taglib/trueaudio/trueaudiofile.h
new file mode 100644 (file)
index 0000000..cbb1755
--- /dev/null
@@ -0,0 +1,180 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_TRUEAUDIOFILE_H
+#define TAGLIB_TRUEAUDIOFILE_H
+
+#include "tfile.h"
+#include "trueaudioproperties.h"
+
+namespace TagLib {
+
+  class Tag;
+
+  namespace ID3v2 { class Tag; class FrameFactory; }
+  namespace ID3v1 { class Tag; }
+
+  //! An implementation of TrueAudio metadata
+
+  /*!
+   * This is implementation of TrueAudio metadata.
+   *
+   * This supports ID3v1 and ID3v2 tags as well as reading stream
+   * properties from the file.
+   */
+
+  namespace TrueAudio {
+
+    //! An implementation of TagLib::File with TrueAudio specific methods
+
+    /*!
+     * This implements and provides an interface for TrueAudio files to the
+     * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+     * the abstract TagLib::File API as well as providing some additional
+     * information specific to TrueAudio files.
+     */
+
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * This set of flags is used for various operations and is suitable for
+       * being OR-ed together.
+       */
+      enum TagTypes {
+        //! Empty set.  Matches no tag types.
+        NoTags  = 0x0000,
+        //! Matches ID3v1 tags.
+        ID3v1   = 0x0001,
+        //! Matches ID3v2 tags.
+        ID3v2   = 0x0002,
+        //! Matches all tag types.
+        AllTags = 0xffff
+      };
+
+      /*!
+       * Contructs an TrueAudio file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       */
+      File(FileName file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Contructs an TrueAudio file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored. The frames will be created using
+       * \a frameFactory.
+       */
+      File(FileName file, ID3v2::FrameFactory *frameFactory,
+           bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns the Tag for this file.
+       */
+      virtual TagLib::Tag *tag() const;
+
+      /*!
+       * Returns the TrueAudio::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Set the ID3v2::FrameFactory to something other than the default.
+       *
+       * \see ID3v2FrameFactory
+       */
+      void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
+
+      /*!
+       * Saves the file.
+       */
+      virtual bool save();
+
+      /*!
+       * Returns a pointer to the ID3v2 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v2 tag.  If \a create is true it will create
+       * an ID3v1 tag if one does not exist. If there is already an APE tag, the
+       * new ID3v1 tag will be placed after it.
+       *
+       * \note The Tag <b>is still</b> owned by the TrueAudio::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v1::Tag *ID3v1Tag(bool create = false);
+
+      /*!
+       * Returns a pointer to the ID3v1 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v1 tag.  If \a create is true it will create
+       * an ID3v1 tag if one does not exist. If there is already an APE tag, the
+       * new ID3v1 tag will be placed after it.
+       *
+       * \note The Tag <b>is still</b> owned by the TrueAudio::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v2::Tag *ID3v2Tag(bool create = false);
+
+      /*!
+       * This will remove the tags that match the OR-ed together TagTypes from the
+       * file.  By default it removes all tags.
+       *
+       * \note This will also invalidate pointers to the tags
+       * as their memory will be freed.
+       * \note In order to make the removal permanent save() still needs to be called
+       */
+      void strip(int tags = AllTags);
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+      void scan();
+      long findID3v1();
+      long findID3v2();
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/trueaudio/trueaudioproperties.cpp b/src/taglib/trueaudio/trueaudioproperties.cpp
new file mode 100644 (file)
index 0000000..ff65edf
--- /dev/null
@@ -0,0 +1,136 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <bitset>
+
+#include "trueaudioproperties.h"
+#include "trueaudiofile.h"
+
+using namespace TagLib;
+
+class TrueAudio::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
+    data(d),
+    streamLength(length),
+    style(s),
+    version(0),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0),
+    bitsPerSample(0) {}
+
+  ByteVector data;
+  long streamLength;
+  ReadStyle style;
+  int version;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+  int bitsPerSample;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+TrueAudio::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(data, streamLength, style);
+  read();
+}
+
+TrueAudio::Properties::~Properties()
+{
+  delete d;
+}
+
+int TrueAudio::Properties::length() const
+{
+  return d->length;
+}
+
+int TrueAudio::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int TrueAudio::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int TrueAudio::Properties::bitsPerSample() const
+{
+  return d->bitsPerSample;
+}
+
+int TrueAudio::Properties::channels() const
+{
+  return d->channels;
+}
+
+int TrueAudio::Properties::ttaVersion() const
+{
+  return d->version;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void TrueAudio::Properties::read()
+{
+  if(!d->data.startsWith("TTA"))
+    return;
+
+  int pos = 3;
+
+  d->version = d->data[pos] - '0';
+  pos += 1 + 2;
+
+  d->channels = d->data.mid(pos, 2).toShort(false);
+  pos += 2;
+
+  d->bitsPerSample = d->data.mid(pos, 2).toShort(false);
+  pos += 2;
+
+  d->sampleRate = d->data.mid(pos, 4).toUInt(false);
+  pos += 4;
+
+  unsigned long samples = d->data.mid(pos, 4).toUInt(false);
+  d->length = samples / d->sampleRate;
+
+  d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
+}
diff --git a/src/taglib/trueaudio/trueaudioproperties.h b/src/taglib/trueaudio/trueaudioproperties.h
new file mode 100644 (file)
index 0000000..b89fe9d
--- /dev/null
@@ -0,0 +1,93 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_TRUEAUDIOPROPERTIES_H
+#define TAGLIB_TRUEAUDIOPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace TrueAudio {
+
+    class File;
+
+    static const uint HeaderSize = 18;
+
+    //! An implementation of audio property reading for TrueAudio
+
+    /*!
+     * This reads the data from an TrueAudio stream found in the AudioProperties
+     * API.
+     */
+
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      /*!
+       * Create an instance of TrueAudio::Properties with the data read from the
+       * ByteVector \a data.
+       */
+      Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
+
+      /*!
+       * Destroys this TrueAudio::Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+      /*!
+       * Returns number of bits per sample.
+       */
+      int bitsPerSample() const;
+
+      /*!
+       * Returns the major version number.
+       */
+      int ttaVersion() const;
+
+    private:
+      Properties(const Properties &);
+      Properties &operator=(const Properties &);
+
+      void read();
+
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/wavpack/wavpackfile.cpp b/src/taglib/wavpack/wavpackfile.cpp
new file mode 100644 (file)
index 0000000..9a64b8c
--- /dev/null
@@ -0,0 +1,270 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tdebug.h>
+#include <tagunion.h>
+
+#include "wavpackfile.h"
+#include "id3v1tag.h"
+#include "id3v2header.h"
+#include "apetag.h"
+#include "apefooter.h"
+
+using namespace TagLib;
+
+namespace
+{
+  enum { APEIndex, ID3v1Index };
+}
+
+class WavPack::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    APELocation(-1),
+    APESize(0),
+    ID3v1Location(-1),
+    properties(0),
+    scanned(false),
+    hasAPE(false),
+    hasID3v1(false) {}
+
+  ~FilePrivate()
+  {
+    delete properties;
+  }
+
+  long APELocation;
+  uint APESize;
+
+  long ID3v1Location;
+
+  TagUnion tag;
+
+  Properties *properties;
+  bool scanned;
+
+  // These indicate whether the file *on disk* has these tags, not if
+  // this data structure does.  This is used in computing offsets.
+
+  bool hasAPE;
+  bool hasID3v1;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+WavPack::File::File(FileName file, bool readProperties,
+                Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+WavPack::File::~File()
+{
+  delete d;
+}
+
+TagLib::Tag *WavPack::File::tag() const
+{
+  return &d->tag;
+}
+
+WavPack::Properties *WavPack::File::audioProperties() const
+{
+  return d->properties;
+}
+
+bool WavPack::File::save()
+{
+  if(readOnly()) {
+    debug("WavPack::File::save() -- File is read only.");
+    return false;
+  }
+
+  // Update ID3v1 tag
+
+  if(ID3v1Tag()) {
+    if(d->hasID3v1) {
+      seek(d->ID3v1Location);
+      writeBlock(ID3v1Tag()->render());
+    }
+    else {
+      seek(0, End);
+      d->ID3v1Location = tell();
+      writeBlock(ID3v1Tag()->render());
+      d->hasID3v1 = true;
+    }
+  }
+  else {
+    if(d->hasID3v1) {
+      removeBlock(d->ID3v1Location, 128);
+      d->hasID3v1 = false;
+      if(d->hasAPE) {
+        if(d->APELocation > d->ID3v1Location)
+          d->APELocation -= 128;
+      }
+    }
+  }
+
+  // Update APE tag
+
+  if(APETag()) {
+    if(d->hasAPE)
+      insert(APETag()->render(), d->APELocation, d->APESize);
+    else {
+      if(d->hasID3v1)  {
+        insert(APETag()->render(), d->ID3v1Location, 0);
+        d->APESize = APETag()->footer()->completeTagSize();
+        d->hasAPE = true;
+        d->APELocation = d->ID3v1Location;
+        d->ID3v1Location += d->APESize;
+      }
+      else {
+        seek(0, End);
+        d->APELocation = tell();
+        writeBlock(APETag()->render());
+        d->APESize = APETag()->footer()->completeTagSize();
+        d->hasAPE = true;
+      }
+    }
+  }
+  else {
+    if(d->hasAPE) {
+      removeBlock(d->APELocation, d->APESize);
+      d->hasAPE = false;
+      if(d->hasID3v1) {
+        if(d->ID3v1Location > d->APELocation) {
+          d->ID3v1Location -= d->APESize;
+        }
+      }
+    }
+  }
+
+   return true;
+}
+
+ID3v1::Tag *WavPack::File::ID3v1Tag(bool create)
+{
+  return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
+}
+
+APE::Tag *WavPack::File::APETag(bool create)
+{
+  return d->tag.access<APE::Tag>(APEIndex, create);
+}
+
+void WavPack::File::strip(int tags)
+{
+  if(tags & ID3v1) {
+    d->tag.set(ID3v1Index, 0);
+    APETag(true);
+  }
+
+  if(tags & APE) {
+    d->tag.set(APEIndex, 0);
+
+    if(!ID3v1Tag())
+      APETag(true);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
+{
+  // Look for an ID3v1 tag
+
+  d->ID3v1Location = findID3v1();
+
+  if(d->ID3v1Location >= 0) {
+    d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
+    d->hasID3v1 = true;
+  }
+
+  // Look for an APE tag
+
+  d->APELocation = findAPE();
+
+  if(d->APELocation >= 0) {
+    d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
+    d->APESize = APETag()->footer()->completeTagSize();
+    d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
+    d->hasAPE = true;
+  }
+
+  if(!d->hasID3v1)
+    APETag(true);
+
+  // Look for WavPack audio properties
+
+  if(readProperties) {
+    seek(0);
+    d->properties = new Properties(readBlock(WavPack::HeaderSize),
+                                   length() - d->APESize);
+  }
+}
+
+long WavPack::File::findAPE()
+{
+  if(!isValid())
+    return -1;
+
+  if(d->hasID3v1)
+    seek(-160, End);
+  else
+    seek(-32, End);
+
+  long p = tell();
+
+  if(readBlock(8) == APE::Tag::fileIdentifier())
+    return p;
+
+  return -1;
+}
+
+long WavPack::File::findID3v1()
+{
+  if(!isValid())
+    return -1;
+
+  seek(-128, End);
+  long p = tell();
+
+  if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+    return p;
+
+  return -1;
+}
diff --git a/src/taglib/wavpack/wavpackfile.h b/src/taglib/wavpack/wavpackfile.h
new file mode 100644 (file)
index 0000000..46bd3e8
--- /dev/null
@@ -0,0 +1,164 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_WVFILE_H
+#define TAGLIB_WVFILE_H
+
+#include "tfile.h"
+#include "taglib_export.h"
+#include "wavpackproperties.h"
+
+namespace TagLib {
+
+  class Tag;
+
+  namespace ID3v1 { class Tag; }
+  namespace APE { class Tag; }
+
+  //! An implementation of WavPack metadata
+
+  /*!
+   * This is implementation of WavPack metadata.
+   *
+   * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
+   * properties from the file.
+   */
+
+  namespace WavPack {
+
+    //! An implementation of TagLib::File with WavPack specific methods
+
+    /*!
+     * This implements and provides an interface for WavPack files to the
+     * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+     * the abstract TagLib::File API as well as providing some additional
+     * information specific to WavPack files.
+     */
+
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * This set of flags is used for various operations and is suitable for
+       * being OR-ed together.
+       */
+      enum TagTypes {
+        //! Empty set.  Matches no tag types.
+        NoTags  = 0x0000,
+        //! Matches ID3v1 tags.
+        ID3v1   = 0x0001,
+        //! Matches APE tags.
+        APE     = 0x0002,
+        //! Matches all tag types.
+        AllTags = 0xffff
+      };
+
+      /*!
+       * Contructs an WavPack file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       */
+      File(FileName file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns the Tag for this file.  This will be an APE tag, an ID3v1 tag
+       * or a combination of the two.
+       */
+      virtual TagLib::Tag *tag() const;
+
+      /*!
+       * Returns the MPC::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Saves the file.
+       */
+      virtual bool save();
+
+      /*!
+       * Returns a pointer to the ID3v1 tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid ID3v1 tag.  If \a create is true it will create
+       * an ID3v1 tag if one does not exist. If there is already an APE tag, the
+       * new ID3v1 tag will be placed after it.
+       *
+       * \note The Tag <b>is still</b> owned by the APE::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      ID3v1::Tag *ID3v1Tag(bool create = false);
+
+      /*!
+       * Returns a pointer to the APE tag of the file.
+       *
+       * If \a create is false (the default) this will return a null pointer
+       * if there is no valid APE tag.  If \a create is true it will create
+       * a APE tag if one does not exist.
+       *
+       * \note The Tag <b>is still</b> owned by the APE::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      APE::Tag *APETag(bool create = false);
+
+      /*!
+       * This will remove the tags that match the OR-ed together TagTypes from the
+       * file.  By default it removes all tags.
+       *
+       * \note This will also invalidate pointers to the tags
+       * as their memory will be freed.
+       * \note In order to make the removal permanent save() still needs to be called
+       */
+      void strip(int tags = AllTags);
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+      void scan();
+      long findID3v1();
+      long findAPE();
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/src/taglib/wavpack/wavpackproperties.cpp b/src/taglib/wavpack/wavpackproperties.cpp
new file mode 100644 (file)
index 0000000..ec7a9bf
--- /dev/null
@@ -0,0 +1,143 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <bitset>
+
+#include "wavpackproperties.h"
+#include "wavpackfile.h"
+
+using namespace TagLib;
+
+class WavPack::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
+    data(d),
+    streamLength(length),
+    style(s),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0),
+    version(0),
+    bitsPerSample(0) {}
+
+  ByteVector data;
+  long streamLength;
+  ReadStyle style;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+  int version;
+  int bitsPerSample;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+WavPack::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(data, streamLength, style);
+  read();
+}
+
+WavPack::Properties::~Properties()
+{
+  delete d;
+}
+
+int WavPack::Properties::length() const
+{
+  return d->length;
+}
+
+int WavPack::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int WavPack::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int WavPack::Properties::channels() const
+{
+  return d->channels;
+}
+
+int WavPack::Properties::version() const
+{
+  return d->version;
+}
+
+int WavPack::Properties::bitsPerSample() const
+{
+  return d->bitsPerSample;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+static const unsigned int sample_rates[] = { 6000, 8000, 9600, 11025, 12000,
+    16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };
+
+#define BYTES_STORED    3
+#define MONO_FLAG       4
+
+#define SHIFT_LSB       13
+#define SHIFT_MASK      (0x1fL << SHIFT_LSB)
+
+#define SRATE_LSB       23
+#define SRATE_MASK      (0xfL << SRATE_LSB)
+
+void WavPack::Properties::read()
+{
+  if(!d->data.startsWith("wvpk"))
+    return;
+
+  d->version = d->data.mid(8, 2).toShort(false);
+
+  unsigned int flags = d->data.mid(24, 4).toUInt(false);
+  d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 -
+    ((flags & SHIFT_MASK) >> SHIFT_LSB);
+  d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB];
+  d->channels = (flags & MONO_FLAG) ? 1 : 2;
+
+  unsigned int samples = d->data.mid(12, 4).toUInt(false);
+  d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
+
+  d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
+}
+
diff --git a/src/taglib/wavpack/wavpackproperties.h b/src/taglib/wavpack/wavpackproperties.h
new file mode 100644 (file)
index 0000000..a77e4de
--- /dev/null
@@ -0,0 +1,94 @@
+/***************************************************************************
+    copyright            : (C) 2006 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+                           (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_WVPROPERTIES_H
+#define TAGLIB_WVPROPERTIES_H
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace WavPack {
+
+    class File;
+
+    static const uint HeaderSize = 32;
+
+    //! An implementation of audio property reading for WavPack
+
+    /*!
+     * This reads the data from an WavPack stream found in the AudioProperties
+     * API.
+     */
+
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      /*!
+       * Create an instance of WavPack::Properties with the data read from the
+       * ByteVector \a data.
+       */
+      Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
+
+      /*!
+       * Destroys this WavPack::Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+      /*!
+       * Returns number of bits per sample.
+       */
+      int bitsPerSample() const;
+
+      /*!
+       * Returns WavPack version.
+       */
+      int version() const;
+
+    private:
+      Properties(const Properties &);
+      Properties &operator=(const Properties &);
+
+      void read();
+
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+  }
+}
+
+#endif
index 85634d7..9ab07a4 100644 (file)
 
 #include "tagresolver.h"
 #include <QDebug>
+#include <QFile>
+#include <tag.h>
+#include <fileref.h>
 
 using namespace SomePlayer::DataObjects;
 
 TagResolver::TagResolver(QObject *parent) :
     QObject(parent)
 {
-       _metaObject = new Phonon::MediaObject(this);
-       connect(_metaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(metaStateChanged(Phonon::State,Phonon::State)));
 }
 
 void TagResolver::decode(QStringList files) {
-       _sources.clear();
        foreach (QString filename, files) {
-               _sources.append(Phonon::MediaSource(filename));
-       }
-       if (!_sources.isEmpty()) {
-               _metaObject->setCurrentSource(_sources.at(0));
-       }
-}
-
-void TagResolver::metaStateChanged(Phonon::State newState, Phonon::State /*oldState*/) {
-       if (newState == Phonon::StoppedState) {
-               int time = _metaObject->totalTime();
-               Phonon::MediaSource source = _metaObject->currentSource();
-               QMap<QString, QString> meta = _metaObject->metaData();
-               TrackMetadata metadata(meta.value("TITLE"), meta.value("ARTIST"), meta.value("ALBUM"), time/1000);
-               Track track(0, metadata, source.fileName());
-               int index = _sources.indexOf(source)+1;
-               emit decoded(track);
-               _metaObject->stop();
-               if (index != _sources.size()) {
-                       Phonon::MediaSource newSource = _sources.at(index);
-                       _metaObject->clear();
-                       _metaObject->setCurrentSource(newSource);
-               } else {
-                       emit done();
+               TagLib::FileRef file_ref(QFile::encodeName(filename).data());
+               if (!file_ref.isNull()) {
+                       TagLib::Tag *tag = file_ref.tag();
+                       if (NULL != tag) {
+                               TagLib::AudioProperties *properties = file_ref.audioProperties();
+                               if (NULL != properties) {
+                                       TrackMetadata meta(QString::fromStdWString(tag->title().toWString()),
+                                                          QString::fromStdWString(tag->artist().toWString()),
+                                                          QString::fromStdWString(tag->album().toWString()),
+                                                          properties->length());
+                                       Track track(0, meta, filename);
+                                       emit decoded(track);
+                               }
+                       }
                }
-       } else if (newState == Phonon::ErrorState) {
-               Phonon::MediaSource s = _metaObject->currentSource();
-               _metaObject->clear();
-               _metaObject->setCurrentSource(s);
        }
+       emit done();
 }
index 86f1789..31facd9 100644 (file)
@@ -23,8 +23,6 @@
 #include <QObject>
 #include "someplayer.h"
 #include "track.h"
-#include <phonon/MediaObject>
-#include <phonon/MediaSource>
 
 using SomePlayer::DataObjects::Track;
 
@@ -43,14 +41,6 @@ namespace SomePlayer {
                signals:
                        void decoded(Track);
                        void done();
-
-               private slots:
-                       void metaStateChanged(Phonon::State newState, Phonon::State /*oldState*/);
-               private:
-                       QStringList _files;
-                       Phonon::MediaObject *_metaObject;
-                       QList<Phonon::MediaSource> _sources;
-
                };
        };
 };
index 9fbae8b..c7ffbd2 100644 (file)
    <string>Form</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
-   <item row="0" column="0">
+   <item row="1" column="0">
     <widget class="QLabel" name="label">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
      <property name="text">
       <string>BUSY</string>
      </property>
      </property>
     </widget>
    </item>
+   <item row="2" column="0">
+    <widget class="QProgressBar" name="progressBar">
+     <property name="value">
+      <number>24</number>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="0">
+    <spacer name="verticalSpacer_2">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="3" column="0">
+    <spacer name="verticalSpacer_3">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
   </layout>
  </widget>
  <resources/>