[f-spot/taglib-metadata: 1/3] Taglib# sync e78bc6160d12b957134fc9274d0e3852f5e2e560



commit 6d953d1b98bf072e45a6261023d7c5eff66fa527
Author: Ruben Vermeersch <ruben savanne be>
Date:   Sun May 16 09:37:34 2010 +0200

    Taglib# sync e78bc6160d12b957134fc9274d0e3852f5e2e560
    
    Initial import of Taglib#

 configure.ac                                       |    1 +
 lib/Makefile.am                                    |   33 +
 lib/TagLib/Makefile.am                             |  203 ++
 lib/TagLib/TagLib/Aac/AudioHeader.cs               |  433 ++++
 lib/TagLib/TagLib/Aac/BitStream.cs                 |  110 +
 lib/TagLib/TagLib/Aac/File.cs                      |  282 +++
 lib/TagLib/TagLib/Aiff/File.cs                     |  481 ++++
 lib/TagLib/TagLib/Aiff/StreamHeader.cs             |  324 +++
 lib/TagLib/TagLib/Ape/File.cs                      |  280 +++
 lib/TagLib/TagLib/Ape/Footer.cs                    |  418 ++++
 lib/TagLib/TagLib/Ape/Item.cs                      |  532 ++++
 lib/TagLib/TagLib/Ape/StreamHeader.cs              |  372 +++
 lib/TagLib/TagLib/Ape/Tag.cs                       | 1563 ++++++++++++
 lib/TagLib/TagLib/Asf/ContentDescriptionObject.cs  |  270 ++
 lib/TagLib/TagLib/Asf/ContentDescriptor.cs         |  494 ++++
 lib/TagLib/TagLib/Asf/DescriptionRecord.cs         |  617 +++++
 .../TagLib/Asf/ExtendedContentDescriptionObject.cs |  276 ++
 lib/TagLib/TagLib/Asf/File.cs                      |  431 ++++
 lib/TagLib/TagLib/Asf/FilePropertiesObject.cs      |  319 +++
 lib/TagLib/TagLib/Asf/Guid.cs                      |  111 +
 lib/TagLib/TagLib/Asf/HeaderExtensionObject.cs     |  176 ++
 lib/TagLib/TagLib/Asf/HeaderObject.cs              |  275 ++
 lib/TagLib/TagLib/Asf/MetadataLibraryObject.cs     |  298 +++
 lib/TagLib/TagLib/Asf/Object.cs                    |  253 ++
 lib/TagLib/TagLib/Asf/PaddingObject.cs             |  139 +
 lib/TagLib/TagLib/Asf/StreamPropertiesObject.cs    |  270 ++
 lib/TagLib/TagLib/Asf/Tag.cs                       | 1416 +++++++++++
 lib/TagLib/TagLib/Asf/UnknownObject.cs             |  110 +
 lib/TagLib/TagLib/ByteVector.cs                    | 2628 ++++++++++++++++++++
 lib/TagLib/TagLib/ByteVectorList.cs                |  281 +++
 lib/TagLib/TagLib/CombinedTag.cs                   | 1513 +++++++++++
 lib/TagLib/TagLib/CorruptFileException.cs          |  168 ++
 lib/TagLib/TagLib/Debugger.cs                      |  144 ++
 lib/TagLib/TagLib/File.cs                          | 1774 +++++++++++++
 lib/TagLib/TagLib/FileTypes.cs                     |  138 +
 lib/TagLib/TagLib/Flac/Block.cs                    |  200 ++
 lib/TagLib/TagLib/Flac/BlockHeader.cs              |  197 ++
 lib/TagLib/TagLib/Flac/File.cs                     |  682 +++++
 lib/TagLib/TagLib/Flac/Picture.cs                  |  332 +++
 lib/TagLib/TagLib/Flac/StreamHeader.cs             |  237 ++
 lib/TagLib/TagLib/Genres.cs                        |  406 +++
 lib/TagLib/TagLib/ICodec.cs                        |  239 ++
 lib/TagLib/TagLib/IFD/Entries/ByteIFDEntry.cs      |  100 +
 .../TagLib/IFD/Entries/ByteVectorIFDEntry.cs       |  100 +
 lib/TagLib/TagLib/IFD/Entries/LongArrayIFDEntry.cs |   89 +
 lib/TagLib/TagLib/IFD/Entries/LongIFDEntry.cs      |  100 +
 lib/TagLib/TagLib/IFD/Entries/MakernoteIFDEntry.cs |  273 ++
 lib/TagLib/TagLib/IFD/Entries/Rational.cs          |  172 ++
 .../TagLib/IFD/Entries/RationalArrayIFDEntry.cs    |   92 +
 lib/TagLib/TagLib/IFD/Entries/RationalIFDEntry.cs  |  104 +
 lib/TagLib/TagLib/IFD/Entries/SByteIFDEntry.cs     |  100 +
 .../TagLib/IFD/Entries/SLongArrayIFDEntry.cs       |   89 +
 lib/TagLib/TagLib/IFD/Entries/SLongIFDEntry.cs     |  100 +
 lib/TagLib/TagLib/IFD/Entries/SRational.cs         |  173 ++
 lib/TagLib/TagLib/IFD/Entries/SRationalIFDEntry.cs |  104 +
 .../TagLib/IFD/Entries/SShortArrayIFDEntry.cs      |   89 +
 lib/TagLib/TagLib/IFD/Entries/SShortIFDEntry.cs    |  100 +
 .../TagLib/IFD/Entries/ShortArrayIFDEntry.cs       |   89 +
 lib/TagLib/TagLib/IFD/Entries/ShortIFDEntry.cs     |  100 +
 lib/TagLib/TagLib/IFD/Entries/StringIFDEntry.cs    |  104 +
 .../TagLib/IFD/Entries/StripOffsetsIFDEntry.cs     |  151 ++
 lib/TagLib/TagLib/IFD/Entries/SubIFDEntry.cs       |  124 +
 .../TagLib/IFD/Entries/ThumbnailDataIFDEntry.cs    |  106 +
 lib/TagLib/TagLib/IFD/Entries/UndefinedIFDEntry.cs |  100 +
 .../TagLib/IFD/Entries/UserCommentIFDEntry.cs      |  178 ++
 lib/TagLib/TagLib/IFD/IFDDirectory.cs              |   35 +
 lib/TagLib/TagLib/IFD/IFDEntry.cs                  |  135 +
 lib/TagLib/TagLib/IFD/IFDEntryType.cs              |  108 +
 lib/TagLib/TagLib/IFD/IFDReader.cs                 |  860 +++++++
 lib/TagLib/TagLib/IFD/IFDRenderer.cs               |  267 ++
 lib/TagLib/TagLib/IFD/IFDStructure.cs              |  466 ++++
 lib/TagLib/TagLib/IFD/IFDTag.cs                    |  545 ++++
 .../TagLib/IFD/Makernotes/Nikon3MakernoteReader.cs |  123 +
 .../TagLib/IFD/Tags/CanonMakerNoteEntryTag.cs      |  159 ++
 lib/TagLib/TagLib/IFD/Tags/ExifEntryTag.cs         |  372 +++
 lib/TagLib/TagLib/IFD/Tags/GPSEntryTag.cs          |  222 ++
 lib/TagLib/TagLib/IFD/Tags/IFDEntryTag.cs          | 1062 ++++++++
 lib/TagLib/TagLib/IFD/Tags/IOPEntryTag.cs          |   61 +
 .../TagLib/IFD/Tags/Nikon3MakerNoteEntryTag.cs     |  473 ++++
 .../IFD/Tags/NikonPreviewMakerNoteEntryTag.cs      |   80 +
 .../TagLib/IFD/Tags/PanasonicMakerNoteEntryTag.cs  |  290 +++
 lib/TagLib/TagLib/Id3v1/StringHandler.cs           |   77 +
 lib/TagLib/TagLib/Id3v1/Tag.cs                     |  500 ++++
 lib/TagLib/TagLib/Id3v2/ExtendedHeader.cs          |  122 +
 lib/TagLib/TagLib/Id3v2/Footer.cs                  |  295 +++
 lib/TagLib/TagLib/Id3v2/Frame.cs                   |  539 ++++
 lib/TagLib/TagLib/Id3v2/FrameFactory.cs            |  283 +++
 lib/TagLib/TagLib/Id3v2/FrameHeader.cs             |  489 ++++
 lib/TagLib/TagLib/Id3v2/FrameTypes.cs              |   85 +
 .../TagLib/Id3v2/Frames/AttachedPictureFrame.cs    |  705 ++++++
 lib/TagLib/TagLib/Id3v2/Frames/CommentsFrame.cs    |  539 ++++
 .../Id3v2/Frames/GeneralEncapsulatedObjectFrame.cs |  450 ++++
 .../TagLib/Id3v2/Frames/MusicCdIdentifierFrame.cs  |  328 +++
 lib/TagLib/TagLib/Id3v2/Frames/PlayCountFrame.cs   |  361 +++
 .../TagLib/Id3v2/Frames/PopularimeterFrame.cs      |  297 +++
 lib/TagLib/TagLib/Id3v2/Frames/PrivateFrame.cs     |  432 ++++
 .../TagLib/Id3v2/Frames/RelativeVolumeFrame.cs     |  610 +++++
 .../TagLib/Id3v2/Frames/SynchronizedLyricsFrame.cs |  722 ++++++
 lib/TagLib/TagLib/Id3v2/Frames/TermsOfUseFrame.cs  |  390 +++
 .../TagLib/Id3v2/Frames/TextIdentificationFrame.cs | 1344 ++++++++++
 .../Id3v2/Frames/UniqueFileIdentifierFrame.cs      |  325 +++
 lib/TagLib/TagLib/Id3v2/Frames/UnknownFrame.cs     |  208 ++
 .../Id3v2/Frames/UnsynchronisedLyricsFrame.cs      |  530 ++++
 lib/TagLib/TagLib/Id3v2/Header.cs                  |  320 +++
 lib/TagLib/TagLib/Id3v2/SynchData.cs               |  144 ++
 lib/TagLib/TagLib/Id3v2/Tag.cs                     | 2069 +++++++++++++++
 lib/TagLib/TagLib/Image/Codec.cs                   |  145 ++
 lib/TagLib/TagLib/Image/CombinedImageTag.cs        |  533 ++++
 lib/TagLib/TagLib/Image/File.cs                    |  185 ++
 lib/TagLib/TagLib/Image/ImageOrientation.cs        |   87 +
 lib/TagLib/TagLib/Image/ImageTag.cs                |  222 ++
 lib/TagLib/TagLib/IntList.cs                       |   20 +
 lib/TagLib/TagLib/Jpeg/Codec.cs                    |   66 +
 lib/TagLib/TagLib/Jpeg/File.cs                     |  812 ++++++
 lib/TagLib/TagLib/Jpeg/JpegCommentTag.cs           |  101 +
 lib/TagLib/TagLib/Jpeg/Marker.cs                   |  349 +++
 lib/TagLib/TagLib/Jpeg/Table.cs                    |   64 +
 lib/TagLib/TagLib/ListBase.cs                      |  459 ++++
 lib/TagLib/TagLib/Mpc/File.cs                      |  278 +++
 lib/TagLib/TagLib/Mpc/StreamHeader.cs              |  363 +++
 lib/TagLib/TagLib/Mpeg/AudioFile.cs                |  296 +++
 lib/TagLib/TagLib/Mpeg/AudioHeader.cs              |  785 ++++++
 lib/TagLib/TagLib/Mpeg/File.cs                     |  697 ++++++
 lib/TagLib/TagLib/Mpeg/VBRIHeader.cs               |  205 ++
 lib/TagLib/TagLib/Mpeg/VideoHeader.cs              |  206 ++
 lib/TagLib/TagLib/Mpeg/XingHeader.cs               |  221 ++
 lib/TagLib/TagLib/Mpeg4/AppleTag.cs                | 1434 +++++++++++
 lib/TagLib/TagLib/Mpeg4/Box.cs                     |  592 +++++
 lib/TagLib/TagLib/Mpeg4/BoxFactory.cs              |  265 ++
 lib/TagLib/TagLib/Mpeg4/BoxHeader.cs               |  424 ++++
 lib/TagLib/TagLib/Mpeg4/BoxTypes.cs                |   95 +
 .../TagLib/Mpeg4/Boxes/AppleAdditionalInfoBox.cs   |  119 +
 .../TagLib/Mpeg4/Boxes/AppleAnnotationBox.cs       |  110 +
 lib/TagLib/TagLib/Mpeg4/Boxes/AppleDataBox.cs      |  207 ++
 .../Mpeg4/Boxes/AppleElementaryStreamDescriptor.cs |  314 +++
 lib/TagLib/TagLib/Mpeg4/Boxes/AppleItemListBox.cs  |  105 +
 lib/TagLib/TagLib/Mpeg4/Boxes/FullBox.cs           |  210 ++
 .../TagLib/Mpeg4/Boxes/IsoAudioSampleEntry.cs      |  237 ++
 .../TagLib/Mpeg4/Boxes/IsoChunkLargeOffsetBox.cs   |  190 ++
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoChunkOffsetBox.cs |  187 ++
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoFreeSpaceBox.cs   |  117 +
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoHandlerBox.cs     |  174 ++
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoMetaBox.cs        |  128 +
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoMovieHeaderBox.cs |  248 ++
 .../TagLib/Mpeg4/Boxes/IsoSampleDescriptionBox.cs  |  132 +
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleEntry.cs    |  109 +
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleTableBox.cs |   97 +
 lib/TagLib/TagLib/Mpeg4/Boxes/IsoUserDataBox.cs    |  106 +
 .../TagLib/Mpeg4/Boxes/IsoVisualSampleEntry.cs     |  197 ++
 lib/TagLib/TagLib/Mpeg4/Boxes/UnknownBox.cs        |   97 +
 lib/TagLib/TagLib/Mpeg4/File.cs                    |  419 ++++
 lib/TagLib/TagLib/Mpeg4/FileParser.cs              |  603 +++++
 lib/TagLib/TagLib/NonContainer/EndTag.cs           |  368 +++
 lib/TagLib/TagLib/NonContainer/File.cs             |  401 +++
 lib/TagLib/TagLib/NonContainer/StartTag.cs         |  330 +++
 lib/TagLib/TagLib/NonContainer/Tag.cs              |  266 ++
 lib/TagLib/TagLib/Ogg/Bitstream.cs                 |  227 ++
 lib/TagLib/TagLib/Ogg/Codec.cs                     |  287 +++
 lib/TagLib/TagLib/Ogg/Codecs/Theora.cs             |  381 +++
 lib/TagLib/TagLib/Ogg/Codecs/Vorbis.cs             |  356 +++
 lib/TagLib/TagLib/Ogg/File.cs                      |  443 ++++
 lib/TagLib/TagLib/Ogg/GroupedComment.cs            | 1269 ++++++++++
 lib/TagLib/TagLib/Ogg/Page.cs                      |  299 +++
 lib/TagLib/TagLib/Ogg/PageHeader.cs                |  559 +++++
 lib/TagLib/TagLib/Ogg/Paginator.cs                 |  275 ++
 lib/TagLib/TagLib/Ogg/XiphComment.cs               | 1204 +++++++++
 lib/TagLib/TagLib/Picture.cs                       |  450 ++++
 lib/TagLib/TagLib/Properties.cs                    |  484 ++++
 lib/TagLib/TagLib/ReadOnlyByteVector.cs            |  216 ++
 lib/TagLib/TagLib/Riff/AviHeaderList.cs            |  364 +++
 lib/TagLib/TagLib/Riff/AviStream.cs                |  595 +++++
 lib/TagLib/TagLib/Riff/BitmapInfoHeader.cs         |  853 +++++++
 lib/TagLib/TagLib/Riff/DivXTag.cs                  |  406 +++
 lib/TagLib/TagLib/Riff/File.cs                     |  644 +++++
 lib/TagLib/TagLib/Riff/InfoTag.cs                  |  313 +++
 lib/TagLib/TagLib/Riff/List.cs                     |  610 +++++
 lib/TagLib/TagLib/Riff/ListTag.cs                  |  497 ++++
 lib/TagLib/TagLib/Riff/MovieIdTag.cs               |  237 ++
 lib/TagLib/TagLib/Riff/WaveFormatEx.cs             |  775 ++++++
 lib/TagLib/TagLib/StringList.cs                    |  157 ++
 lib/TagLib/TagLib/SupportedMimeType.cs             |  205 ++
 lib/TagLib/TagLib/Tag.cs                           | 1327 ++++++++++
 lib/TagLib/TagLib/TagLib.sources                   |  189 ++
 lib/TagLib/TagLib/Tiff/Codec.cs                    |   61 +
 lib/TagLib/TagLib/Tiff/File.cs                     |  327 +++
 lib/TagLib/TagLib/UnsupportedFormatException.cs    |  164 ++
 lib/TagLib/TagLib/WavPack/File.cs                  |  277 ++
 lib/TagLib/TagLib/WavPack/StreamHeader.cs          |  366 +++
 lib/TagLib/TagLib/Xmp/XmlNodeExtensions.cs         |   97 +
 lib/TagLib/TagLib/Xmp/XmpNode.cs                   |  431 ++++
 lib/TagLib/TagLib/Xmp/XmpNodeType.cs               |   59 +
 lib/TagLib/TagLib/Xmp/XmpNodeVisitor.cs            |   41 +
 lib/TagLib/TagLib/Xmp/XmpTag.cs                    | 1111 +++++++++
 193 files changed, 70516 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 4432aa5..009e377 100644
--- a/configure.ac
+++ b/configure.ac
@@ -362,6 +362,7 @@ lib/semweb/Makefile
 lib/unique-sharp/Makefile
 lib/unique-sharp/generator/Makefile
 lib/unique-sharp/unique/Makefile
+lib/TagLib/Makefile
 docs/Makefile
 icons/Makefile
 tools/Makefile
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 83fa6f8..2a29eca 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -9,6 +9,7 @@ UNCONDITIONAL_SUBDIRS = \
 	libfspot			\
 	libgphoto2-sharp	\
 	semweb				\
+    TagLib 				\
 	unique-sharp
 
 # dpap-sharp isn't built, but it is shipped.
@@ -20,3 +21,35 @@ DIST_SUBDIRS = 			\
 	dpap-sharp			\
 	$(UNCONDITIONAL_SUBDIRS)
 
+
+### The stuff below takes care of embedding libraries. Build system hacks! ###
+TMPDIR_TAGLIB = /tmp/fetch_taglib
+
+clone-taglib:
+	mkdir $(TMPDIR_TAGLIB)
+	git clone --depth 1 git://gitorious.org/taglib-sharp/mainline.git $(TMPDIR_TAGLIB)
+	pushd $(TMPDIR_TAGLIB); git checkout -b photo-support origin/photo-support; popd
+
+
+update-taglib:
+	rm -rf TagLib/TagLib
+	cp -r $(TMPDIR_TAGLIB)/src/TagLib TagLib
+	
+	echo "# This file is autogenerated, do not edit!" > TagLib/Makefile.am
+	echo "include \$$(top_srcdir)/Makefile.include" >> TagLib/Makefile.am
+	echo "ASSEMBLY_NAME = TagLib" >> TagLib/Makefile.am
+	echo >> TagLib/Makefile.am
+	echo "ASSEMBLY_SOURCES = \\" >> TagLib/Makefile.am
+	tail -n+2 TagLib/TagLib/TagLib.sources | sed s/\$$\(srcdir\)\\/// >> TagLib/Makefile.am
+	echo >> TagLib/Makefile.am
+	echo "ASSEMBLY = \$$(ASSEMBLY_NAME).dll" >> TagLib/Makefile.am
+	echo "all: \$$(ASSEMBLY)" >> TagLib/Makefile.am
+	echo "\$$(ASSEMBLY): \$$(ASSEMBLY_SOURCES)" >> TagLib/Makefile.am
+	echo "	\$$(CSC_LIB) \$$(CSC_DEFINES) -out:\$$@ \$$(ASSEMBLY_SOURCES)" >> TagLib/Makefile.am
+	echo >> TagLib/Makefile.am
+	echo "assemblydir = \$$(pkglibdir)" >> TagLib/Makefile.am
+	echo "assembly_DATA = \$$(ASSEMBLY)" >> TagLib/Makefile.am
+	echo "EXTRA_DIST = \$$(ASSEMBLY_SOURCES)" >> TagLib/Makefile.am
+	echo "CLEANFILES = \$$(ASSEMBLY) \$$(ASSEMBLY).mdb" >> TagLib/Makefile.am
+
+.PHONY: clone-taglib update-taglib
diff --git a/lib/TagLib/Makefile.am b/lib/TagLib/Makefile.am
new file mode 100644
index 0000000..604a8b6
--- /dev/null
+++ b/lib/TagLib/Makefile.am
@@ -0,0 +1,203 @@
+# This file is autogenerated, do not edit!
+include $(top_srcdir)/Makefile.include
+ASSEMBLY_NAME = TagLib
+
+ASSEMBLY_SOURCES = \
+	TagLib/Aac/AudioHeader.cs \
+	TagLib/Aac/BitStream.cs \
+	TagLib/Aac/File.cs \
+	TagLib/Aiff/File.cs \
+	TagLib/Aiff/StreamHeader.cs \
+	TagLib/Ape/Tag.cs \
+	TagLib/Ape/Item.cs \
+	TagLib/Ape/Footer.cs \
+	TagLib/Ape/File.cs \
+	TagLib/Ape/StreamHeader.cs \
+	TagLib/Asf/Guid.cs \
+	TagLib/Asf/StreamPropertiesObject.cs \
+	TagLib/Asf/UnknownObject.cs \
+	TagLib/Asf/Tag.cs \
+	TagLib/Asf/Object.cs \
+	TagLib/Asf/ExtendedContentDescriptionObject.cs \
+	TagLib/Asf/ContentDescriptionObject.cs \
+	TagLib/Asf/MetadataLibraryObject.cs \
+	TagLib/Asf/HeaderObject.cs \
+	TagLib/Asf/HeaderExtensionObject.cs \
+	TagLib/Asf/File.cs \
+	TagLib/Asf/FilePropertiesObject.cs \
+	TagLib/Asf/PaddingObject.cs \
+	TagLib/Asf/ContentDescriptor.cs \
+	TagLib/Asf/DescriptionRecord.cs \
+	TagLib/Mpc/StreamHeader.cs \
+	TagLib/Mpc/File.cs \
+	TagLib/Ogg/GroupedComment.cs \
+	TagLib/Ogg/Bitstream.cs \
+	TagLib/Ogg/Paginator.cs \
+	TagLib/Ogg/Codec.cs \
+	TagLib/Ogg/Codecs/Vorbis.cs \
+	TagLib/Ogg/Codecs/Theora.cs \
+	TagLib/Ogg/Page.cs \
+	TagLib/Ogg/XiphComment.cs \
+	TagLib/Ogg/PageHeader.cs \
+	TagLib/Ogg/File.cs \
+	TagLib/Flac/Block.cs \
+	TagLib/Flac/BlockHeader.cs \
+	TagLib/Flac/StreamHeader.cs \
+	TagLib/Flac/Picture.cs \
+	TagLib/Flac/File.cs \
+	TagLib/Image/Codec.cs \
+	TagLib/Image/CombinedImageTag.cs \
+	TagLib/Image/File.cs \
+	TagLib/Image/ImageTag.cs \
+	TagLib/Image/ImageOrientation.cs \
+	TagLib/Jpeg/Codec.cs \
+	TagLib/Jpeg/File.cs \
+	TagLib/Jpeg/JpegCommentTag.cs \
+	TagLib/Jpeg/Marker.cs \
+	TagLib/Jpeg/Table.cs \
+	TagLib/Mpeg/XingHeader.cs \
+	TagLib/Mpeg/VBRIHeader.cs \
+	TagLib/Mpeg/File.cs \
+	TagLib/Mpeg/AudioFile.cs \
+	TagLib/Mpeg/AudioHeader.cs \
+	TagLib/Mpeg/VideoHeader.cs \
+	TagLib/NonContainer/EndTag.cs \
+	TagLib/NonContainer/File.cs \
+	TagLib/NonContainer/StartTag.cs \
+	TagLib/NonContainer/Tag.cs \
+	TagLib/Id3v1/Tag.cs \
+	TagLib/Id3v1/StringHandler.cs \
+	TagLib/Id3v2/Frames/PopularimeterFrame.cs \
+	TagLib/Id3v2/Frames/PlayCountFrame.cs \
+	TagLib/Id3v2/Frames/PrivateFrame.cs \
+	TagLib/Id3v2/Frames/RelativeVolumeFrame.cs \
+	TagLib/Id3v2/Frames/UniqueFileIdentifierFrame.cs \
+	TagLib/Id3v2/Frames/UnknownFrame.cs \
+	TagLib/Id3v2/Frames/CommentsFrame.cs \
+	TagLib/Id3v2/Frames/TextIdentificationFrame.cs \
+	TagLib/Id3v2/Frames/AttachedPictureFrame.cs \
+	TagLib/Id3v2/Frames/GeneralEncapsulatedObjectFrame.cs \
+	TagLib/Id3v2/Frames/UnsynchronisedLyricsFrame.cs \
+	TagLib/Id3v2/Frames/SynchronizedLyricsFrame.cs \
+	TagLib/Id3v2/Frames/MusicCdIdentifierFrame.cs \
+	TagLib/Id3v2/Frames/TermsOfUseFrame.cs \
+	TagLib/Id3v2/FrameFactory.cs \
+	TagLib/Id3v2/Frame.cs \
+	TagLib/Id3v2/FrameTypes.cs \
+	TagLib/Id3v2/Tag.cs \
+	TagLib/Id3v2/FrameHeader.cs \
+	TagLib/Id3v2/ExtendedHeader.cs \
+	TagLib/Id3v2/SynchData.cs \
+	TagLib/Id3v2/Header.cs \
+	TagLib/Id3v2/Footer.cs \
+	TagLib/IFD/Entries/ByteIFDEntry.cs \
+	TagLib/IFD/Entries/ByteVectorIFDEntry.cs \
+	TagLib/IFD/Entries/LongArrayIFDEntry.cs \
+	TagLib/IFD/Entries/LongIFDEntry.cs \
+	TagLib/IFD/Entries/MakernoteIFDEntry.cs \
+	TagLib/IFD/Entries/RationalArrayIFDEntry.cs \
+	TagLib/IFD/Entries/RationalIFDEntry.cs \
+	TagLib/IFD/Entries/Rational.cs \
+	TagLib/IFD/Entries/SByteIFDEntry.cs \
+	TagLib/IFD/Entries/ShortArrayIFDEntry.cs \
+	TagLib/IFD/Entries/ShortIFDEntry.cs \
+	TagLib/IFD/Entries/SLongArrayIFDEntry.cs \
+	TagLib/IFD/Entries/SLongIFDEntry.cs \
+	TagLib/IFD/Entries/SRationalIFDEntry.cs \
+	TagLib/IFD/Entries/SShortArrayIFDEntry.cs \
+	TagLib/IFD/Entries/SShortIFDEntry.cs \
+	TagLib/IFD/Entries/SRational.cs \
+	TagLib/IFD/Entries/StringIFDEntry.cs \
+	TagLib/IFD/Entries/StripOffsetsIFDEntry.cs \
+	TagLib/IFD/Entries/SubIFDEntry.cs \
+	TagLib/IFD/Entries/ThumbnailDataIFDEntry.cs \
+	TagLib/IFD/Entries/UserCommentIFDEntry.cs \
+	TagLib/IFD/Entries/UndefinedIFDEntry.cs \
+	TagLib/IFD/IFDEntry.cs \
+	TagLib/IFD/IFDEntryType.cs \
+	TagLib/IFD/IFDTag.cs \
+	TagLib/IFD/IFDDirectory.cs \
+	TagLib/IFD/IFDStructure.cs \
+	TagLib/IFD/IFDReader.cs \
+	TagLib/IFD/IFDRenderer.cs \
+	TagLib/IFD/Makernotes/Nikon3MakernoteReader.cs \
+	TagLib/IFD/Tags/CanonMakerNoteEntryTag.cs \
+	TagLib/IFD/Tags/ExifEntryTag.cs \
+	TagLib/IFD/Tags/GPSEntryTag.cs \
+	TagLib/IFD/Tags/IFDEntryTag.cs \
+	TagLib/IFD/Tags/IOPEntryTag.cs \
+	TagLib/IFD/Tags/Nikon3MakerNoteEntryTag.cs \
+	TagLib/IFD/Tags/NikonPreviewMakerNoteEntryTag.cs \
+	TagLib/IFD/Tags/PanasonicMakerNoteEntryTag.cs \
+	TagLib/Mpeg4/AppleTag.cs \
+	TagLib/Mpeg4/Box.cs \
+	TagLib/Mpeg4/BoxFactory.cs \
+	TagLib/Mpeg4/BoxHeader.cs \
+	TagLib/Mpeg4/BoxTypes.cs \
+	TagLib/Mpeg4/File.cs \
+	TagLib/Mpeg4/FileParser.cs \
+	TagLib/Mpeg4/Boxes/IsoFreeSpaceBox.cs \
+	TagLib/Mpeg4/Boxes/UnknownBox.cs \
+	TagLib/Mpeg4/Boxes/IsoUserDataBox.cs \
+	TagLib/Mpeg4/Boxes/IsoChunkOffsetBox.cs \
+	TagLib/Mpeg4/Boxes/IsoChunkLargeOffsetBox.cs \
+	TagLib/Mpeg4/Boxes/AppleItemListBox.cs \
+	TagLib/Mpeg4/Boxes/AppleAnnotationBox.cs \
+	TagLib/Mpeg4/Boxes/IsoSampleTableBox.cs \
+	TagLib/Mpeg4/Boxes/IsoSampleEntry.cs \
+	TagLib/Mpeg4/Boxes/IsoAudioSampleEntry.cs \
+	TagLib/Mpeg4/Boxes/IsoVisualSampleEntry.cs \
+	TagLib/Mpeg4/Boxes/IsoMetaBox.cs \
+	TagLib/Mpeg4/Boxes/IsoSampleDescriptionBox.cs \
+	TagLib/Mpeg4/Boxes/AppleAdditionalInfoBox.cs \
+	TagLib/Mpeg4/Boxes/IsoHandlerBox.cs \
+	TagLib/Mpeg4/Boxes/IsoMovieHeaderBox.cs \
+	TagLib/Mpeg4/Boxes/FullBox.cs \
+	TagLib/Mpeg4/Boxes/AppleElementaryStreamDescriptor.cs \
+	TagLib/Mpeg4/Boxes/AppleDataBox.cs \
+	TagLib/Riff/AviHeaderList.cs \
+	TagLib/Riff/AviStream.cs \
+	TagLib/Riff/WaveFormatEx.cs \
+	TagLib/Riff/BitmapInfoHeader.cs \
+	TagLib/Riff/DivXTag.cs \
+	TagLib/Riff/File.cs \
+	TagLib/Riff/InfoTag.cs \
+	TagLib/Riff/List.cs \
+	TagLib/Riff/ListTag.cs \
+	TagLib/Riff/MovieIdTag.cs \
+	TagLib/Tiff/Codec.cs \
+	TagLib/Tiff/File.cs \
+	TagLib/WavPack/File.cs \
+	TagLib/WavPack/StreamHeader.cs \
+	TagLib/Xmp/XmlNodeExtensions.cs \
+	TagLib/Xmp/XmpTag.cs \
+	TagLib/Xmp/XmpNode.cs \
+	TagLib/Xmp/XmpNodeType.cs \
+	TagLib/Xmp/XmpNodeVisitor.cs \
+	TagLib/ICodec.cs \
+	TagLib/Tag.cs \
+	TagLib/ReadOnlyByteVector.cs \
+	TagLib/ByteVector.cs \
+	TagLib/ByteVectorList.cs \
+	TagLib/CombinedTag.cs \
+	TagLib/Genres.cs \
+	TagLib/Properties.cs \
+	TagLib/File.cs \
+	TagLib/StringList.cs \
+	TagLib/SupportedMimeType.cs \
+	TagLib/UnsupportedFormatException.cs \
+	TagLib/Picture.cs \
+	TagLib/ListBase.cs \
+	TagLib/FileTypes.cs \
+	TagLib/CorruptFileException.cs
+
+
+ASSEMBLY = $(ASSEMBLY_NAME).dll
+all: $(ASSEMBLY)
+$(ASSEMBLY): $(ASSEMBLY_SOURCES)
+	$(CSC_LIB) $(CSC_DEFINES) -out:$@ $(ASSEMBLY_SOURCES)
+
+assemblydir = $(pkglibdir)
+assembly_DATA = $(ASSEMBLY)
+EXTRA_DIST = $(ASSEMBLY_SOURCES)
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
diff --git a/lib/TagLib/TagLib/Aac/AudioHeader.cs b/lib/TagLib/TagLib/Aac/AudioHeader.cs
new file mode 100644
index 0000000..336fe8d
--- /dev/null
+++ b/lib/TagLib/TagLib/Aac/AudioHeader.cs
@@ -0,0 +1,433 @@
+//
+// AudioHeader.cs: Provides information about an ADTS AAC audio stream.
+//
+// Copyright (C) 2009 Patrick Dehne
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Aac
+{
+	/// <summary>
+	///    This structure implements <see cref="IAudioCodec" /> and provides
+	///    information about an ADTS AAC audio stream.
+	/// </summary>
+	public class AudioHeader : IAudioCodec
+	{
+		#region Private Static Value Arrays
+
+		/// <summary>
+		///    Contains a sample rate table for ADTS AAC audio.
+		/// </summary>
+		private static readonly int[] sample_rates = new int[13] {
+			96000, 88200, 64000, 48000, 44100, 32000,
+			24000, 22050, 16000, 12000, 11025, 8000, 7350
+		};
+
+		/// <summary>
+		///    Contains a channel table for ADTS AAC audio.
+		/// </summary>
+		private static readonly int[] channels = new int[8] {
+			0, 1, 2, 3, 4, 5, 6, 8
+		};
+
+		#endregion
+
+
+
+		#region Private Properties
+
+		/// <summary>
+		///    Contains the audio stream length.
+		/// </summary>
+		private long stream_length;
+
+		/// <summary>
+		///    Contains the audio stream duration.
+		/// </summary>
+		private TimeSpan duration;
+
+		/// <summary>
+		///    Contains the number of channels in the audio
+		/// </summary>
+		private int audiochannels;
+
+		/// <summary>
+		///    Contains the bitrate of the audio stream
+		/// </summary>
+		private int audiobitrate;
+
+		/// <summary>
+		///    Contains the samplerate of the audio stream
+		/// </summary>
+		private int audiosamplerate;
+
+		#endregion
+
+
+
+		#region Public Fields
+
+		/// <summary>
+		///    An empty and unset header.
+		/// </summary>
+		public static readonly AudioHeader Unknown =
+			new AudioHeader();
+
+		#endregion
+
+
+
+		#region Constructors
+
+		/// <summary>
+		///    Constructs and initializes a new empty instance of <see
+		///    cref="AudioHeader" />
+		/// </summary>       
+		private AudioHeader()
+		{
+			this.stream_length = 0;
+			this.duration = TimeSpan.Zero;
+			this.audiochannels = 0;
+			this.audiobitrate = 0;
+			this.audiosamplerate = 0;
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AudioHeader" /> by populating it with specified
+		///    values.
+		/// </summary>
+		/// <param name="channels">
+		///    A <see cref="int" /> value indicating the number
+		///    of channels in the audio stream
+		/// </param>
+		/// <param name="bitrate">
+		///    A <see cref="int" /> value indicating the bitrate
+		///    of  the audio stream
+		/// </param>
+		/// <param name="samplerate">
+		///    A <see cref="int" /> value indicating the samplerate
+		///    of  the audio stream
+		/// </param>
+		/// <param name="numberofsamples">
+		///    A <see cref="int" /> value indicating the number
+		///    of samples in the audio stream
+		/// </param>
+		/// <param name="numberofframes">
+		///    A <see cref="int" /> value indicating the number
+		///    of frames in the audio stream
+		/// </param>
+		private AudioHeader(int channels, int bitrate,
+							int samplerate, int numberofsamples, int numberofframes)
+		{
+			this.duration = TimeSpan.Zero;
+			this.stream_length = 0;
+			this.audiochannels = channels;
+			this.audiobitrate = bitrate;
+			this.audiosamplerate = samplerate;
+		}
+
+		#endregion
+
+
+
+		#region Public Properties
+
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description
+		{
+			get
+			{
+				return "ADTS AAC";
+			}
+		}
+
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes
+		{
+			get { return MediaTypes.Audio; }
+		}
+
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    If <see cref="SetStreamLength" /> has not been called, this 
+		///    value will not be correct.
+		/// </remarks>
+		public TimeSpan Duration
+		{
+			get
+			{
+				return duration;
+			}
+		}
+
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate
+		{
+			get
+			{
+				return audiobitrate;
+			}
+		}
+
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate
+		{
+			get
+			{
+				return audiosamplerate;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels
+		{
+			get { return audiochannels; }
+		}
+
+		#endregion
+
+
+
+		#region Public Methods
+
+		/// <summary>
+		///    Sets the length of the audio stream represented by the
+		///    current instance.
+		/// </summary>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value specifying the length in
+		///    bytes of the audio stream represented by the current
+		///    instance.
+		/// </param>
+		/// <remarks>
+		///    The this value has been set, <see cref="Duration" /> will
+		///    return an incorrect value.
+		/// </remarks>
+		public void SetStreamLength(long streamLength)
+		{
+			this.stream_length = streamLength;
+			duration = TimeSpan.FromSeconds(((double)this.stream_length) * 8.0 / ((double)this.audiobitrate));
+		}
+
+		#endregion
+
+
+
+		#region Public Static Methods
+
+		/// <summary>
+		///    Searches for an audio header in a <see cref="TagLib.File"
+		///    /> starting at a specified position and searching through
+		///    a specified number of bytes.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="AudioHeader" /> object in which the found
+		///    header will be stored.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to search.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    in <paramref name="file" /> at which to start searching.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the maximum number
+		///    of bytes to search before aborting.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not a
+		///    header was found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public static bool Find(out AudioHeader header,
+							    TagLib.File file, long position, int length)
+		{
+			if (file == null)
+				throw new ArgumentNullException("file");
+
+			long end = position + length;
+			header = AudioHeader.Unknown;
+
+			file.Seek(position);
+
+			ByteVector buffer = file.ReadBlock(3);
+
+			if (buffer.Count < 3)
+				return false;
+
+			do
+			{
+				file.Seek(position + 3);
+				buffer = buffer.Mid(buffer.Count - 3);
+				buffer.Add(file.ReadBlock(
+					(int)File.BufferSize));
+
+				for (int i = 0; i < buffer.Count - 3 &&
+					(length < 0 || position + i < end); i++)
+					if (buffer[i] == 0xFF
+						&& buffer[i+1] >= 0xF0) // 0xFFF
+						try
+						{                            
+							BitStream bits = new BitStream(buffer.Mid(i, 7).Data);
+
+							// 12 bits sync header 
+							bits.ReadInt32(12);
+
+							// 1 bit mpeg 2/4
+							bits.ReadInt32(1);
+
+							// 2 bits layer
+							bits.ReadInt32(2);
+
+							// 1 bit protection absent  
+							bits.ReadInt32(1);
+						  
+							// 2 bits profile object type
+							bits.ReadInt32(2);
+
+							// 4 bits sampling frequency index                            
+							int samplerateindex = bits.ReadInt32(4);
+							if(samplerateindex >= sample_rates.Length)
+								return false;
+							long samplerate = sample_rates[samplerateindex];
+
+							// 1 bit private bit
+							bits.ReadInt32(1);
+
+							// 3 bits channel configuration
+							int channelconfigindex = bits.ReadInt32(3);
+							if (channelconfigindex >= channels.Length)
+								return false;                            
+
+							// 4 copyright bits
+							bits.ReadInt32(4);
+
+							// 13 bits frame length
+							long framelength = bits.ReadInt32(13); // double check framelength
+								if (framelength < 7)
+								return false;
+
+							// 11 bits buffer fullness
+							bits.ReadInt32(11);
+
+							// 2 bits number of raw data blocks in frame
+							int numberofframes = bits.ReadInt32(2) + 1;
+
+							long numberofsamples = numberofframes * 1024;
+							long bitrate = framelength * 8 * samplerate / numberofsamples;                            
+
+							header = new AudioHeader(channels[channelconfigindex],
+								(int)bitrate,
+								(int)samplerate,
+								(int)numberofsamples,
+								numberofframes);                            
+
+							return true;
+						}
+						catch (CorruptFileException)
+						{
+						}
+
+				position += File.BufferSize;
+			} while (buffer.Count > 3 && (length < 0 || position < end));
+
+			return false;
+		}
+
+		/// <summary>
+		///    Searches for an audio header in a <see cref="TagLib.File"
+		///    /> starting at a specified position and searching to the
+		///    end of the file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="AudioHeader" /> object in which the found
+		///    header will be stored.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to search.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    in <paramref name="file" /> at which to start searching.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not a
+		///    header was found.
+		/// </returns>
+		/// <remarks>
+		///    Searching to the end of the file can be very, very slow
+		///    especially for corrupt or non-MPEG files. It is
+		///    recommended to use <see
+		///    cref="Find(AudioHeader,TagLib.File,long,int)" />
+		///    instead.
+		/// </remarks>
+		public static bool Find(out AudioHeader header,
+							    TagLib.File file, long position)
+		{
+			return Find(out header, file, position, -1);
+		}
+
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Aac/BitStream.cs b/lib/TagLib/TagLib/Aac/BitStream.cs
new file mode 100644
index 0000000..0279867
--- /dev/null
+++ b/lib/TagLib/TagLib/Aac/BitStream.cs
@@ -0,0 +1,110 @@
+//
+// BitStream.cs: Helper to read bits from a byte array.
+//
+// Copyright (C) 2009 Patrick Dehne
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections;
+using System.Diagnostics;
+
+namespace TagLib.Aac
+{
+	/// <summary>
+	///    This class is used to help reading arbitary number of bits from
+	///    a fixed array of bytes
+	/// </summary>
+	public class BitStream
+	{
+		#region Private Fields
+
+		private BitArray bits;
+		private int bitindex;
+
+		#endregion
+
+
+			
+		#region Constructors
+
+		/// <summary>
+		///    Construct a new <see cref="BitStream"/>.
+		/// </summary>
+		/// <param name="buffer">
+		///    A <see cref="System.Byte[]"/>, must be 7 bytes long.
+		/// </param>
+		public BitStream(byte[] buffer)
+		{
+			Debug.Assert(buffer.Length == 7, "buffer.Length == 7", "buffer size invalid");
+			
+			if (buffer.Length != 7)
+				throw new ArgumentException("Buffer size must be 7 bytes");
+
+			// Reverse bits            
+			bits = new BitArray(buffer.Length * 8);
+			for (int i = 0; i < buffer.Length; i++)
+			{
+				for (int y = 0; y < 8; y++)
+				{
+					bits[i * 8 + y] = ((buffer[i] & (1 << (7 - y))) > 0);
+				}
+			}
+			
+			bitindex = 0;
+		}
+
+		#endregion
+
+
+
+		#region Public Methods
+		
+		/// <summary>
+		///    Reads an Int32 from the bitstream        
+		/// </summary>
+		/// <param name="numberOfBits">
+		///    A <see cref="int" /> value containing the number
+		///    of bits to read from the bitstream
+		/// </param>
+		public int ReadInt32(int numberOfBits)
+		{
+			Debug.Assert(numberOfBits > 0, "numberOfBits < 1");
+			Debug.Assert(numberOfBits <= 32, "numberOfBits <= 32");
+
+			if (numberOfBits <= 0)
+				throw new ArgumentException("Number of bits to read must be >= 1");
+
+			if (numberOfBits > 32)
+				throw new ArgumentException("Number of bits to read must be <= 32");
+
+			int value = 0;
+			int start = bitindex + numberOfBits - 1;
+			for (int i = 0; i < numberOfBits; i++)
+			{
+				value += bits[start] ? (1 << i) : 0;
+				bitindex++;
+				start--;
+			}
+
+			return value;
+		}
+
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Aac/File.cs b/lib/TagLib/TagLib/Aac/File.cs
new file mode 100644
index 0000000..d9d14eb
--- /dev/null
+++ b/lib/TagLib/TagLib/Aac/File.cs
@@ -0,0 +1,282 @@
+//
+// File.cs: Provides tagging and properties support for ADTS AAC files
+//
+// Here is the ADTS Header description used for implementation:
+// http://www.hydrogenaudio.org/forums/lofiversion/index.php/t21617.html
+//
+// Copyright (C) 2009 Patrick Dehne
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TagLib.Aac
+{
+	/// <summary>
+	///    This class extends <see cref="TagLib.NonContainer.File" /> to
+	///    provide tagging and properties support for ADTS AAC audio files.
+	/// </summary>
+	/// <remarks>
+	///    A <see cref="TagLib.Id3v1.Tag" /> and <see
+	///    cref="TagLib.Id3v2.Tag" /> will be added automatically to any
+	///    file that doesn't contain one. This change does not effect the
+	///    file until it is saved and can be reversed using the following
+	///    method:
+	///    <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
+	/// </remarks>
+	[SupportedMimeType("taglib/aac", "aac")]
+	[SupportedMimeType("audio/aac")]
+	public class File : TagLib.NonContainer.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the first audio header.
+		/// </summary>
+		private AudioHeader first_header;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local
+		///    file system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local
+		///    file system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="TagLib.File.IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+					 ReadStyle propertiesStyle)
+			: base (abstraction, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with
+		///    an average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="TagLib.File.IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: base (abstraction)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    If a <see cref="TagLib.Id3v2.Tag" /> is added to the
+		///    current instance, it will be placed at the start of the
+		///    file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
+		///    <see cref="TagLib.Ape.Tag" /> will be added to the end of
+		///    the file. All other tag types will be ignored.
+		/// </remarks>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
+			
+			if (t != null || !create)
+				return t;
+			
+			switch (type)
+			{
+			case TagTypes.Id3v1:
+				return EndTag.AddTag (type, Tag);
+			
+			case TagTypes.Id3v2:
+				return StartTag.AddTag (type, Tag);
+			
+			case TagTypes.Ape:
+				return EndTag.AddTag (type, Tag);
+			
+			default:
+				return null;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <remarks>
+		///    This method only searches for an audio header in the
+		///    first 16384 bytes of code to avoid searching forever in
+		///    corrupt files.
+		/// </remarks>
+		protected override void ReadStart (long start,
+										   ReadStyle propertiesStyle)
+		{
+			// Only check the first 16 bytes so we're not stuck
+			// reading a bad file forever.
+			if (propertiesStyle != ReadStyle.None &&
+				!AudioHeader.Find (out first_header, this,
+					start, 0x4000))
+				throw new CorruptFileException (
+					"ADTS audio header not found.");
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadEnd (long end,
+										 ReadStyle propertiesStyle)
+		{
+			// Make sure we have ID3v1 and ID3v2 tags.
+			GetTag (TagTypes.Id3v1, true);
+			GetTag (TagTypes.Id3v2, true);
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		protected override Properties ReadProperties (long start,
+													  long end,
+													  ReadStyle propertiesStyle)
+		{
+			first_header.SetStreamLength (end - start);
+			return new Properties (TimeSpan.Zero, first_header);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Aiff/File.cs b/lib/TagLib/TagLib/Aiff/File.cs
new file mode 100644
index 0000000..55bd63e
--- /dev/null
+++ b/lib/TagLib/TagLib/Aiff/File.cs
@@ -0,0 +1,481 @@
+//
+// File.cs: Provides tagging and properties support for Apple's AIFF 
+// files.
+//
+// Author:
+//   Helmut Wahrmann
+//
+// Copyright (C) 2009 Helmut Wahrmann
+//
+// 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
+//
+
+using System;
+using TagLib.Id3v2;
+
+namespace TagLib.Aiff
+{
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide
+	///    support for reading and writing tags and properties for files
+	///    using the AIFF file format.
+	/// </summary>
+	[SupportedMimeType("taglib/aif", "aif")]
+	[SupportedMimeType("audio/x-aiff")]
+	[SupportedMimeType("audio/aiff")]
+	[SupportedMimeType("sound/aiff")]
+	[SupportedMimeType("application/x-aiff")]
+	public class File : TagLib.File
+	{
+		#region Private Fields
+
+		/// <summary>
+		///    Contains the address of the AIFF header block.
+		/// </summary>
+		private ByteVector header_block = null;
+
+		/// <summary>
+		///  Contains the Id3v2 tag.
+		/// </summary>
+		private Id3v2.Tag tag = null;
+
+		/// <summary>
+		///  Contains the media properties.
+		/// </summary>
+		private Properties properties = null;
+
+		#endregion
+
+		#region Public Static Fields
+
+		/// <summary>
+		///    The identifier used to recognize a AIFF files.
+		/// </summary>
+		/// <value>
+		///    "FORM"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "FORM";
+
+		/// <summary>
+		///    The identifier used to recognize a AIFF Common chunk.
+		/// </summary>
+		/// <value>
+		///    "COMM"
+		/// </value>
+		public static readonly ReadOnlyByteVector CommIdentifier = "COMM";
+
+		/// <summary>
+		///    The identifier used to recognize a AIFF Sound Data Chunk.
+		/// </summary>
+		/// <value>
+		///    "SSND"
+		/// </value>
+		public static readonly ReadOnlyByteVector SoundIdentifier = "SSND";
+
+		/// <summary>
+		///    The identifier used to recognize a AIFF ID3 chunk.
+		/// </summary>
+		/// <value>
+		///    "ID3 "
+		/// </value>
+		public static readonly ReadOnlyByteVector ID3Identifier = "ID3 ";
+
+		#endregion
+
+		#region Public Constructors
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File(string path, ReadStyle propertiesStyle)
+			: this(new File.LocalFileAbstraction(path),
+			       propertiesStyle)
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File(string path)
+			: this(path, ReadStyle.Average)
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File(File.IFileAbstraction abstraction,
+		            ReadStyle propertiesStyle)
+			: base(abstraction)
+		{
+			Mode = AccessMode.Read;
+			try
+			{
+				uint aiff_size;
+				long tag_start, tag_end;
+				Read(true, propertiesStyle, out aiff_size,
+				     out tag_start, out tag_end);
+			}
+			finally
+			{
+				Mode = AccessMode.Closed;
+			}
+
+			TagTypesOnDisk = TagTypes;
+
+			GetTag(TagTypes.Id3v2, true);
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File(File.IFileAbstraction abstraction)
+			: this(abstraction, ReadStyle.Average)
+		{
+		}
+
+		#endregion
+
+		#region Public Properties
+
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override Tag Tag
+		{
+			get { return tag; }
+		}
+
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties
+		{
+			get { return properties; }
+		}
+
+		#endregion
+
+		#region Public Methods
+
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save()
+		{
+			Mode = AccessMode.Write;
+			try
+			{
+				ByteVector data = new ByteVector();
+
+				// Add the ID3 chunk and ID32 tag to the vector
+				if (tag != null)
+				{
+					ByteVector tag_data = tag.Render();
+					if (tag_data.Count > 10)
+					{
+						if (tag_data.Count%2 == 1)
+							tag_data.Add(0);
+
+						data.Add("ID3 ");
+						data.Add(ByteVector.FromUInt(
+						         	(uint) tag_data.Count,
+						         	true));
+						data.Add(tag_data);
+					}
+				}
+
+				// Read the file to determine the current AIFF
+				// size and the area tagging is in.
+				uint aiff_size;
+				long tag_start, tag_end;
+				Read(false, ReadStyle.None, out aiff_size,
+				     out tag_start, out tag_end);
+
+				// If tagging info cannot be found, place it at
+				// the end of the file.
+				if (tag_start < 12 || tag_end < tag_start)
+					tag_start = tag_end = Length;
+
+				int length = (int) (tag_end - tag_start + 8);
+
+				// Insert the tagging data.
+				Insert(data, tag_start, length);
+
+				// If the data size changed update the aiff size.
+				if (data.Count - length != 0 &&
+				    tag_start <= aiff_size)
+				{
+					// Depending, if a Tag has been added or removed, 
+					// the length needs to be adjusted
+					if (tag == null)
+					{
+						length -= 16;
+					}
+					else
+					{
+						length -= 8;
+					}
+
+					Insert(ByteVector.FromUInt((uint)
+					                           (aiff_size + data.Count - length),
+					                           true), 4, 4);
+				}
+				// Update the tag types.
+				TagTypesOnDisk = TagTypes;
+			}
+			finally
+			{
+				Mode = AccessMode.Closed;
+			}
+		}
+
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags(TagTypes types)
+		{
+			if (types == TagLib.TagTypes.Id3v2 ||
+			    types == TagLib.TagTypes.AllTags)
+			{
+				tag = null;
+			}
+		}
+
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public override TagLib.Tag GetTag(TagTypes type, bool create)
+		{
+			TagLib.Tag id32_tag = null;
+
+			switch (type)
+			{
+				case TagTypes.Id3v2:
+					if (tag == null && create)
+					{
+						tag = new Id3v2.Tag();
+						tag.Version = 2;
+					}
+
+					id32_tag = tag;
+					break;
+			}
+
+			return id32_tag;
+		}
+
+		#endregion
+
+		#region Private Methods
+
+		/// <summary>
+		///    Reads the contents of the current instance determining
+		///    the size of the riff data, the area the tagging is in,
+		///    and optionally reading in the tags and media properties.
+		/// </summary>
+		/// <param name="read_tags">
+		///    If <see langword="true" />, any tags found will be read
+		///    into the current instance.
+		/// </param>
+		/// <param name="style">
+		///    A <see cref="ReadStyle"/> value specifying how the media
+		///    data is to be read into the current instance.
+		/// </param>
+		/// <param name="aiff_size">
+		///    A <see cref="uint"/> value reference to be filled with
+		///    the size of the RIFF data as read from the file.
+		/// </param>
+		/// <param name="tag_start">
+		///    A <see cref="long" /> value reference to be filled with
+		///    the absolute seek position at which the tagging data
+		///    starts.
+		/// </param>
+		/// <param name="tag_end">
+		///    A <see cref="long" /> value reference to be filled with
+		///    the absolute seek position at which the tagging data
+		///    ends.
+		/// </param>
+		/// <exception cref="CorruptFileException">
+		///    The file does not begin with <see cref="FileIdentifier"
+		///    />.
+		/// </exception>
+		private void Read(bool read_tags, ReadStyle style,
+		                  out uint aiff_size, out long tag_start,
+		                  out long tag_end)
+		{
+			Seek(0);
+			if (ReadBlock(4) != FileIdentifier)
+				throw new CorruptFileException(
+					"File does not begin with AIFF identifier");
+
+			aiff_size = ReadBlock(4).ToUInt(true);
+			tag_start = -1;
+			tag_end = -1;
+
+			// Get the properties of the file
+			if (header_block == null &&
+			    style != ReadStyle.None)
+			{
+				long common_chunk_pos = Find(CommIdentifier, 0);
+
+				if (common_chunk_pos == -1)
+				{
+					throw new CorruptFileException(
+						"No Common chunk available in AIFF file.");
+				}
+
+				Seek(common_chunk_pos);
+				header_block = ReadBlock((int) StreamHeader.Size);
+
+				StreamHeader header = new StreamHeader(header_block, aiff_size);
+				properties = new Properties(TimeSpan.Zero, header);
+			}
+
+			// Now we search for the ID3 chunk.
+			// Normally it appears after the Sound data chunk. But as the order of
+			// chunks is free, it might be the case that the ID3 chunk appears before 
+			// the sound data chunk.
+			// So we search first for the Sound data chunk and see, if an ID3 chunk appears before
+			long id3_chunk_pos = -1;
+			long sound_chunk_pos = Find(SoundIdentifier, 0, ID3Identifier);
+			if (sound_chunk_pos == -1)
+			{
+				// The ID3 chunk appears before the Sound chunk
+				id3_chunk_pos = Find(ID3Identifier, 0);
+			}
+
+			// Now let's look for the Sound chunk again
+			// Since a previous return value of -1 does mean, that the ID3 chunk was found first
+			sound_chunk_pos = Find(SoundIdentifier, 0);
+			if (sound_chunk_pos == -1)
+			{
+				throw new CorruptFileException(
+					"No Sound chunk available in AIFF file.");
+			}
+
+			// Get the length of the Sound chunk and use this as a start value to look for the ID3 chunk
+			Seek(sound_chunk_pos + 4);
+			ulong sound_chunk_length = ReadBlock(4).ToULong(true);
+			long start_search_pos = (long) sound_chunk_length + sound_chunk_pos + 4;
+
+			if (id3_chunk_pos == -1)
+			{
+				id3_chunk_pos = Find(ID3Identifier, start_search_pos);
+			}
+
+			if (id3_chunk_pos > -1)
+			{
+				if (read_tags && tag == null)
+				{
+					tag = new Id3v2.Tag(this,
+					                    id3_chunk_pos + 8);
+				}
+
+				// Get the length of the tag out of the ID3 chunk
+				Seek(id3_chunk_pos + 4);
+				uint tag_size = ReadBlock(4).ToUInt(true) + 8;
+
+				tag_start = InvariantStartPosition = id3_chunk_pos;
+				tag_end = InvariantEndPosition = tag_start + tag_size;
+			}
+		}
+
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Aiff/StreamHeader.cs b/lib/TagLib/TagLib/Aiff/StreamHeader.cs
new file mode 100644
index 0000000..c7d32b7
--- /dev/null
+++ b/lib/TagLib/TagLib/Aiff/StreamHeader.cs
@@ -0,0 +1,324 @@
+//
+// StreamHeader.cs: Provides support for reading Apple's AIFF stream
+// properties.
+//
+// Author:
+//   Helmut Wahrmann
+//
+// Copyright (C) 2009 Helmut Wahrmann
+//
+// 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
+//
+
+using System;
+using System.Globalization;
+
+namespace TagLib.Aiff
+{
+	/// <summary>
+	///    This struct implements <see cref="IAudioCodec" /> to provide
+	///    support for reading Apple's AIFF stream properties.
+	/// </summary>
+	public struct StreamHeader : IAudioCodec, ILosslessAudioCodec
+	{
+		#region Private Fields
+
+		/// <summary>
+		///    Contains the number of channels.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (9,10).
+		///    1 is monophonic, 2 is stereo, 4 means 4 channels, etc..
+		///    any number of audio channels may be represented
+		/// </remarks>
+		private ushort channels;
+
+		/// <summary>
+		///    Contains the number of sample frames in the Sound Data chunk.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (11-14).
+		/// </remarks>
+		private ulong total_frames;
+
+		/// <summary>
+		///    Contains the number of bits per sample.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (15,16).
+		///    It can be any number from 1 to 32.
+		/// </remarks>
+		private ushort bits_per_sample;
+
+		/// <summary>
+		///    Contains the sample rate.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (17-26).
+		///    the sample rate at which the sound is to be played back, 
+		///    in sample frames per second
+		/// </remarks>
+		private ulong sample_rate;
+
+		/// <summary>
+		///    Contains the length of the audio stream.
+		/// </summary>
+		/// <remarks>
+		///    This value is provided by the constructor.
+		/// </remarks>
+		private long stream_length;
+
+		#endregion
+
+		#region Public Static Fields
+
+		/// <summary>
+		///    The size of an AIFF Common chunk
+		/// </summary>
+		public const uint Size = 26;
+
+		/// <summary>
+		///    The identifier used to recognize a AIFF file.
+		///    Altough an AIFF file start with "FORM2, we're interested
+		///    in the Common chunk only, which contains the properties we need.
+		/// </summary>
+		/// <value>
+		///    "COMM"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier =
+			"COMM";
+
+		#endregion
+
+		#region Constructors
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StreamHeader" /> for a specified header block and
+		///    stream length.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the stream
+		///    header data.
+		/// </param>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value containing the length of the
+		///    AIFF Audio stream in bytes.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> does not begin with <see
+		///    cref="FileIdentifier" /> 
+		/// </exception>
+		public StreamHeader(ByteVector data, long streamLength)
+		{
+			if (data == null)
+				throw new ArgumentNullException("data");
+
+
+			if (!data.StartsWith(FileIdentifier))
+				throw new CorruptFileException(
+					"Data does not begin with identifier.");
+
+			stream_length = streamLength;
+
+			// The first 8 bytes contain the Common chunk identifier "COMM"
+			// And the size of the common chunk, which is always 18
+			channels = data.Mid(8, 2).ToUShort(true);
+			total_frames = data.Mid(10, 4).ToULong(true);
+			bits_per_sample = data.Mid(14, 2).ToUShort(true);
+
+			ByteVector sample_rate_indicator = data.Mid(17, 1);
+			ulong sample_rate_tmp = data.Mid(18, 2).ToULong(true);
+			sample_rate = 44100; // Set 44100 as default sample rate
+
+			// The following are combinations that iTunes 8 encodes to.
+			// There may be other combinations in the field, but i couldn't test them.
+			switch (sample_rate_tmp)
+			{
+				case 44100:
+					if (sample_rate_indicator == 0x0E)
+					{
+						sample_rate = 44100;
+					}
+					else if (sample_rate_indicator == 0x0D)
+					{
+						sample_rate = 22050;
+					}
+					else if (sample_rate_indicator == 0x0C)
+					{
+						sample_rate = 11025;
+					}
+					break;
+
+				case 48000:
+					if (sample_rate_indicator == 0x0E)
+					{
+						sample_rate = 48000;
+					}
+					else if (sample_rate_indicator == 0x0D)
+					{
+						sample_rate = 24000;
+					}
+					break;
+
+				case 64000:
+					if (sample_rate_indicator == 0x0D)
+					{
+						sample_rate = 32000;
+					}
+					else if (sample_rate_indicator == 0x0C)
+					{
+						sample_rate = 16000;
+					}
+					else if (sample_rate_indicator == 0x0B)
+					{
+						sample_rate = 8000;
+					}
+					break;
+
+				case 44510:
+					if (sample_rate_indicator == 0x0D)
+					{
+						sample_rate = 22255;
+					}
+					break;
+
+				case 44508:
+					if (sample_rate_indicator == 0x0C)
+					{
+						sample_rate = 11127;
+					}
+					break;
+			}
+		}
+
+		#endregion
+
+		#region Public Properties
+
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		public TimeSpan Duration
+		{
+			get
+			{
+				if (sample_rate <= 0 || total_frames <= 0)
+					return TimeSpan.Zero;
+
+				return TimeSpan.FromSeconds(
+					(double) total_frames/
+					(double) sample_rate);
+			}
+		}
+
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes
+		{
+			get { return MediaTypes.Audio; }
+		}
+
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description
+		{
+			get { return "AIFF Audio"; }
+		}
+
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate
+		{
+			get
+			{
+				TimeSpan d = Duration;
+				if (d <= TimeSpan.Zero)
+					return 0;
+
+				return (int) ((stream_length*8L)/
+				              d.TotalSeconds)/1000;
+			}
+		}
+
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate
+		{
+			get { return (int) sample_rate; }
+		}
+
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels
+		{
+			get { return channels; }
+		}
+
+		/// <summary>
+		///    Gets the number of bits per sample in the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of bits
+		///    per sample in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int BitsPerSample
+		{
+			get { return bits_per_sample; }
+		}
+
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ape/File.cs b/lib/TagLib/TagLib/Ape/File.cs
new file mode 100644
index 0000000..31f61ec
--- /dev/null
+++ b/lib/TagLib/TagLib/Ape/File.cs
@@ -0,0 +1,280 @@
+//
+// File.cs: Provides tagging and properties support for Monkey's Audio APE
+// files.
+//
+// Author:
+//   Helmut Wahrmann
+//
+// Copyright (C) 2007 Helmut Wahrmann
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Ape {
+	/// <summary>
+	///    This class extends <see cref="TagLib.NonContainer.File" /> to
+	///    provide tagging and properties support for Monkey's Audio APE
+	///    files.
+	/// </summary>
+	/// <remarks>
+	///    A <see cref="TagLib.Ape.Tag" /> will be added automatically to
+	///    any file that doesn't contain one. This change does not effect
+	///    the physical file until <see cref="Save" /> is called and can be
+	///    reversed using the following method:
+	///    <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
+	/// </remarks>
+	[SupportedMimeType("taglib/ape", "ape")]
+	[SupportedMimeType("audio/x-ape")]
+	[SupportedMimeType("audio/ape")]
+	[SupportedMimeType("application/x-ape")]
+	public class File : TagLib.NonContainer.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the block with the audio header.
+		/// </summary>
+		private ByteVector header_block = null;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path)
+			: base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle)
+			: base (abstraction, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: base (abstraction)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    If a <see cref="TagLib.Id3v2.Tag" /> is added to the
+		///    current instance, it will be placed at the start of the
+		///    file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
+		///    <see cref="TagLib.Ape.Tag" /> will be added to the end of
+		///    the file. All other tag types will be ignored.
+		/// </remarks>
+		public override TagLib.Tag GetTag(TagTypes type, bool create)
+		{
+			TagLib.Tag t = (Tag as TagLib.NonContainer.Tag)
+				.GetTag (type);
+			
+			if (t != null || !create)
+				return t;
+			
+			switch (type)
+			{
+			case TagTypes.Id3v1:
+				return EndTag.AddTag (type, Tag);
+			
+			case TagTypes.Id3v2:
+				return StartTag.AddTag (type, Tag);
+			
+			case TagTypes.Ape:
+				return EndTag.AddTag (type, Tag);
+			
+			default:
+				return null;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadStart (long start,
+		                                   ReadStyle propertiesStyle)
+		{
+			if (header_block != null &&
+				propertiesStyle == ReadStyle.None)
+				return;
+			
+			Seek(start);
+				header_block = ReadBlock ((int)StreamHeader.Size);
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadEnd (long end,
+		                                 ReadStyle propertiesStyle)
+		{
+			// Make sure we have an APE tag.
+			GetTag (TagTypes.Ape, true);
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		protected override Properties ReadProperties (long start,
+		                                              long end,
+		                                              ReadStyle propertiesStyle)
+		{
+			StreamHeader header = new StreamHeader (header_block,
+				end - start);
+			
+			return new Properties(TimeSpan.Zero, header);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ape/Footer.cs b/lib/TagLib/TagLib/Ape/Footer.cs
new file mode 100644
index 0000000..c55626b
--- /dev/null
+++ b/lib/TagLib/TagLib/Ape/Footer.cs
@@ -0,0 +1,418 @@
+//
+// Footer.cs: Provides a representation of an APEv2 tag footer which can be read
+// from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   apefooter.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2004 Allan Sandfeld Jensen (Original Implementation)
+// copyright (C) 2002, 2003 Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Ape {
+	#region Enums
+	
+	/// <summary>
+	///    Indicates the flags applied to a <see cref="Footer" /> object.
+	/// </summary>
+	[Flags]
+	public enum FooterFlags : uint {
+		/// <summary>
+		///    The tag lacks a footer object.
+		/// </summary>
+		FooterAbsent  = 0x40000000,
+		
+		/// <summary>
+		///    The footer is actually a header.
+		/// </summary>
+		IsHeader      = 0x20000000,
+		
+		/// <summary>
+		///    The tag contains a header.
+		/// </summary>
+		HeaderPresent = 0x80000000
+	}
+	
+	#endregion
+	
+	
+	
+	/// <summary>
+	///    This structure provides a representation of an APEv2 tag footer
+	///    which can be read from and written to disk.
+	/// </summary>
+	public struct Footer : IEquatable<Footer>
+	{
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the APE tag version.
+		/// </summary>
+		private uint version;
+		
+		/// <summary>
+		///    Contains the footer flags.
+		/// </summary>
+		private FooterFlags flags;
+		
+		/// <summary>
+		///    Contains the number of items in the tag.
+		/// </summary>
+		private uint item_count;
+		
+		/// <summary>
+		///    Contains the tag size including the footer but excluding
+		///    the header.
+		/// </summary>
+		private uint tag_size;
+		
+		#endregion
+		
+		
+		
+		#region Public Static Fields
+		
+		/// <summary>
+		///    Specifies the size of an APEv2 footer.
+		/// </summary>
+		public const uint Size = 32;
+		
+		/// <summary>
+		///    Specifies the identifier used find an APEv2 footer in a
+		///    file.
+		/// </summary>
+		/// <value>
+		///    "<c>APETAGEX</c>"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "APETAGEX";
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Footer" /> by reading it from raw footer data.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data to build the new instance from.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> is smaller than <see
+		///    cref="Size" /> or does not begin with <see
+		///    cref="FileIdentifier" />.
+		/// </exception>
+		public Footer (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"Provided data is smaller than object size.");
+			
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"Provided data does not start with File Identifier");
+			
+			version = data.Mid (8, 4).ToUInt (false);
+			tag_size = data.Mid (12, 4).ToUInt (false);
+			item_count = data.Mid (16, 4).ToUInt (false);
+			flags = (FooterFlags) data.Mid (20, 4).ToUInt (false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the version of APE tag described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the version of the
+		///    APE tag described by the current instance.
+		/// </value>
+		public uint Version {
+			get {return version == 0 ? 2000 : version;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the flags that apply to the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="FooterFlags" /> value
+		///    containing the flags that apply to the current instance.
+		/// </value>
+		public FooterFlags Flags {
+			get {return flags;}
+			set {flags = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of items in the tag represented
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    items in the tag represented by the current instance.
+		/// </value>
+		public uint ItemCount {
+			get {return item_count;}
+			set {item_count = value;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the tag represented by the current
+		///    instance, including the footer but excluding the header
+		///    if applicable.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    tag represented by the current instance.
+		/// </value>
+		public uint TagSize {
+			get {return tag_size;}
+			set {tag_size = value;}
+		}
+		
+		/// <summary>
+		///    Gets the complete size of the tag represented by the
+		///    current instance, including the header and footer.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    tag represented by the current instance.
+		/// </value>
+		public uint CompleteTagSize {
+			get {
+				return TagSize + ((Flags &
+					FooterFlags.HeaderPresent) != 0 ?
+					Size : 0);
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as an APE tag footer.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector RenderFooter ()
+		{
+			return Render (false);
+		}
+		
+		/// <summary>
+		///    Renders the current instance as an APE tag header.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance or an empty
+		///    <see cref="ByteVector" /> object if <see cref="Flags" />
+		///    does not include <see cref="FooterFlags.HeaderPresent"
+		///    />.
+		/// </returns>
+		public ByteVector RenderHeader ()
+		{
+			return (Flags & FooterFlags.HeaderPresent) != 0 ?
+				Render (true) : new ByteVector ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Renders the current instance as either an APE tag header
+		///    or footer.
+		/// </summary>
+		/// <param name="isHeader">
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is to be rendered as a header.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		private ByteVector Render (bool isHeader)
+		{
+			ByteVector v = new ByteVector ();
+			
+			// add the file identifier -- "APETAGEX"
+			v.Add (FileIdentifier);
+			
+			// add the version number -- we always render a 2.000
+			// tag regardless of what the tag originally was.
+			v.Add (ByteVector.FromUInt (2000, false));
+			
+			// add the tag size
+			v.Add (ByteVector.FromUInt (tag_size, false));
+			
+			// add the item count
+			v.Add (ByteVector.FromUInt (item_count, false));
+			
+			// render and add the flags
+			uint flags = 0;
+			
+			if ((Flags & FooterFlags.HeaderPresent) != 0)
+				flags |= (uint) FooterFlags.HeaderPresent;
+			
+			// footer is always present
+			if (isHeader)
+				flags |= (uint) FooterFlags.IsHeader;
+			else
+				flags &= (uint) ~FooterFlags.IsHeader;
+			
+			v.Add (ByteVector.FromUInt (flags, false));
+			
+			// add the reserved 64bit
+			v.Add (ByteVector.FromULong (0));
+			
+			return v;
+		}
+		
+		#endregion
+		
+		
+		
+		#region IEquatable
+		
+		/// <summary>
+		///    Generates a hash code for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value containing the hash code for
+		///    the current instance.
+		/// </returns>
+		public override int GetHashCode ()
+		{
+			unchecked {
+				return (int) ((uint) flags ^ tag_size ^
+					item_count ^ version);
+			}
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another object.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="object" /> to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public override bool Equals (object other)
+		{
+			if (!(other is Footer))
+				return false;
+			
+			return Equals ((Footer) other);
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another instance of <see cref="Footer" />.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="Footer" /> object to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public bool Equals (Footer other)
+		{
+			return flags == other.flags &&
+				tag_size == other.tag_size &&
+				item_count == other.item_count &&
+				version == other.version;
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see cref="Footer"
+		///    /> are equal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    The first <see cref="Footer" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    The second <see cref="Footer" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    equal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator == (Footer first, Footer second)
+		{
+			return first.Equals (second);
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see cref="Footer"
+		///    /> are unequal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    The first <see cref="Footer" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    The second <see cref="Footer" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    unequal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator != (Footer first, Footer second)
+		{
+			return !first.Equals (second);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ape/Item.cs b/lib/TagLib/TagLib/Ape/Item.cs
new file mode 100644
index 0000000..a21670d
--- /dev/null
+++ b/lib/TagLib/TagLib/Ape/Item.cs
@@ -0,0 +1,532 @@
+//
+// Item.cs: Provides a representation of an APEv2 tag item which can be read
+// from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   apeitem.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2004 by Allan Sandfeld Jensen (Original 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
+//
+
+using System;
+
+namespace TagLib.Ape {
+	/// <summary>
+	///    Indicates the type of data stored in a <see cref="Item" />
+	///    object.
+	/// </summary>
+	public enum ItemType {
+		/// <summary>
+		///    The item contains Unicode text.
+		/// </summary>
+		Text = 0,
+		
+		/// <summary>
+		///    The item contains binary data.
+		/// </summary>
+		Binary = 1,
+		
+		/// <summary>
+		///    The item contains a locator (file path/URL) for external
+		///    information.
+		/// </summary>
+		Locator = 2
+	}
+	
+	/// <summary>
+	///    This class provides a representation of an APEv2 tag item which
+	///    can be read from and written to disk.
+	/// </summary>
+	public class Item : ICloneable
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the type of data stored in the item.
+		/// </summary>
+		private ItemType type = ItemType.Text;
+		
+		/// <summary>
+		///    Contains the item key.
+		/// </summary>
+		private string key = null;
+		
+		/// <summary>
+		///    Contains the item value.
+		/// </summary>
+		private ReadOnlyByteVector data = null;
+		
+		/// <summary>
+		///    Contains the item text.
+		/// </summary>
+		private string [] text = null;
+		
+		/// <summary>
+		///    Indicates whether or not the item is read only.
+		/// </summary>
+		private bool read_only = false;
+		
+		/// <summary>
+		///    Contains the size of the item on disk.
+		/// </summary>
+		private int size_on_disk;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Item" />  by reading in a raw APEv2 item.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the item to
+		///    read.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the offset in
+		///    <paramref name="data" /> at which the item data begins.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    A complete item could not be read.
+		/// </exception>
+		public Item (ByteVector data, int offset)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			Parse (data, offset);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Item" /> with a specified key and value.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key to use
+		///    for the current instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string" /> object containing the value to
+		///    store in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> or <paramref name="value" /> is
+		///    <see langword="null" />.
+		/// </exception>
+		public Item (string key, string value)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (value == null)
+				throw new ArgumentNullException ("value");
+			
+			this.key = key;
+			this.text = new string [] {value};
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Item" /> with a specified key and collection of
+		///    values.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key to use
+		///    for the current instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string[]" /> containing the values to store
+		///    in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> or <paramref name="value" /> is
+		///    <see langword="null" />.
+		/// </exception>
+		public Item (string key, params string [] value)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (value == null)
+				throw new ArgumentNullException ("value");
+			
+			this.key = key;
+			this.text = (string[]) value.Clone ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Item" /> with a specified key and collection of
+		///    values.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key to use
+		///    for the current instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="StringCollection" /> object containing the
+		///    values to store in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> or <paramref name="value" /> is
+		///    <see langword="null" />.
+		/// </exception>
+		/// <seealso cref="Item(string,string[])" />
+		[Obsolete("Use Item(string,string[])")]
+		public Item (string key, StringCollection value)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (value == null)
+				throw new ArgumentNullException ("value");
+			
+			this.key = key;
+			this.text = value.ToArray ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Item" /> with a specified key and raw data.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key to use
+		///    for the current instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="StringCollection" /> object containing the
+		///    values to store in the new instance.
+		/// </param>
+		/// <remarks>
+		///    This constructor automatically marks the new instance as
+		///    <see cref="ItemType.Binary" />.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> or <paramref name="value" /> is
+		///    <see langword="null" />.
+		/// </exception>
+		/// <seealso cref="Item(string,string[])" />
+		public Item (string key, ByteVector value)
+		{
+			this.key = key;
+			this.type = ItemType.Binary;
+			
+			data = value as ReadOnlyByteVector;
+			if (data == null)
+				data = new ReadOnlyByteVector (value);
+		}
+		
+		private Item (Item item)
+		{
+			type = item.type;
+			key = item.key;
+			if (item.data != null)
+				data = new ReadOnlyByteVector (item.data);
+			if (item.text != null)
+				text = (string[]) item.text.Clone ();
+			read_only = item.read_only;
+			size_on_disk = item.size_on_disk;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the key used to identify the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the key used to
+		///    identify the current instance.
+		/// </value>
+		/// <remarks>
+		///    This value is used for specifying the contents of the
+		///    item in a common and consistant fashion. For example,
+		///    <c>"TITLE"</c> specifies that the item contains the title
+		///    of the track.
+		/// </remarks>
+		public string Key {
+			get {return key;}
+		}
+		
+		/// <summary>
+		///    Gets the binary value stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the binary
+		///    value stored in the current instance, or <see
+		///    langword="null" /> if the item contains text.
+		/// </value>
+		public ByteVector Value {
+			get {return (type == ItemType.Binary) ? data : null;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the current instance as it last appeared
+		///    on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the size of the
+		///    current instance as it last appeared on disk.
+		/// </value>
+		public int Size {
+			get {return size_on_disk;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the type of value contained in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ItemType" /> value indicating the type of
+		///    value contained in the current instance.
+		/// </value>
+		public ItemType Type {
+			get {return type;}
+			set {type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets whether or not the current instance is
+		///    flagged as read-only on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is flagged as read-only on disk.
+		/// </value>
+		public bool ReadOnly {
+			get {return read_only;}
+			set {read_only = value;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance contains no value.
+		/// </value>
+		public bool IsEmpty {
+			get {
+				if (type != ItemType.Binary)
+					return text == null || text.Length == 0;
+				else
+					return data == null || data.IsEmpty;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets the contents of the current instance as a <see
+		///    cref="string" />.
+		/// </summary>
+		/// <returns>
+		///    <para>A <see cref="string" /> object containing the text
+		///    stored in the current instance, or <see langword="null"
+		///    /> if the item is empty of contains binary data.</para>
+		///    <para>If the current instance contains multiple string
+		///    values, they will be returned as a comma separated
+		///    value.</para>
+		/// </returns>
+		public override string ToString ()
+		{
+			if (type == ItemType.Binary || text == null)
+				return null;
+			else
+				return string.Join (", ", text);
+		}
+		
+		/// <summary>
+		///    Gets the contents of the current instance as a <see
+		///    cref="string" /> array.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the text stored in
+		///    the current instance, or an empty array if the item
+		///    contains binary data.
+		/// </returns>
+		public string [] ToStringArray ()
+		{
+			if (type == ItemType.Binary || text == null)
+				return new string [0];
+			
+			return text;
+		}
+		
+		/// <summary>
+		///    Renders the current instance as an APEv2 item.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			uint flags = (uint) ((ReadOnly) ? 1 : 0) |
+				((uint) Type << 1);
+			
+			if (IsEmpty)
+				return new ByteVector ();
+			
+			ByteVector result = null;
+			
+			if (type == ItemType.Binary) {
+				if (text == null && data != null)
+					result = data;
+			}
+			
+			if (result == null && text != null) {
+				result = new ByteVector ();
+				
+				for (int i = 0; i < text.Length; i ++) {
+					if (i != 0)
+						result.Add ((byte) 0);
+					
+					result.Add (ByteVector.FromString (
+						text [i], StringType.UTF8));
+				}
+			}
+			
+			// If no data is stored, don't write the item.
+			if (result == null || result.Count == 0)
+				return new ByteVector ();
+			
+			ByteVector output = new ByteVector ();
+			output.Add (ByteVector.FromUInt ((uint) result.Count,
+				false));
+			output.Add (ByteVector.FromUInt (flags, false));
+			output.Add (ByteVector.FromString (key, StringType.UTF8));
+			output.Add ((byte) 0);
+			output.Add (result);
+			
+			size_on_disk = output.Count;
+			
+			return output;
+		}
+		
+		#endregion
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the current instance by reading in a raw APEv2
+		///    item.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the item to
+		///    read.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the offset in
+		///    <paramref name="data" /> at which the item data begins.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    A complete item could not be read.
+		/// </exception>
+		protected void Parse (ByteVector data, int offset)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException ("offset");
+			
+			
+			// 11 bytes is the minimum size for an APE item
+			if(data.Count < offset + 11)
+				throw new CorruptFileException (
+					"Not enough data for APE Item");
+			
+			uint value_length = data.Mid (offset, 4).ToUInt (false);
+			uint flags = data.Mid (offset + 4, 4).ToUInt (false);
+			
+			ReadOnly = (flags & 1) == 1;
+			Type = (ItemType) ((flags >> 1) & 3);
+			
+			int pos = data.Find (ByteVector.TextDelimiter (
+				StringType.UTF8), offset + 8);
+			
+			key = data.ToString (StringType.UTF8,
+				offset + 8, pos - offset - 8);
+			
+			if (value_length > data.Count - pos - 1)
+				throw new CorruptFileException (
+					"Invalid data length.");
+			
+			size_on_disk = pos + 1 + (int) value_length - offset;
+			
+			if (Type == ItemType.Binary)
+				this.data = new ReadOnlyByteVector (
+					data.Mid (pos + 1, (int) value_length));
+			else
+				this.text = data.Mid (pos + 1,
+					(int) value_length).ToStrings (
+						StringType.UTF8, 0);
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Item"/> object identical to the current
+		///    instance.
+		/// </returns>
+		public Item Clone ()
+		{
+			return new Item (this);
+		}
+		
+		object ICloneable.Clone ()
+		{
+			return Clone ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ape/StreamHeader.cs b/lib/TagLib/TagLib/Ape/StreamHeader.cs
new file mode 100644
index 0000000..4481853
--- /dev/null
+++ b/lib/TagLib/TagLib/Ape/StreamHeader.cs
@@ -0,0 +1,372 @@
+//
+// StreamHeader.cs: Provides support for reading Monkey's Audio APE stream
+// properties.
+//
+// Author:
+//   Helmut Wahrmann
+//
+// Copyright (C) 2007 Helmut Wahrmann
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Globalization;
+
+namespace TagLib.Ape {
+	/// <summary>
+	///    Indicates the compression level used when encoding a Monkey's
+	///    Audio APE file.
+	/// </summary>
+	public enum CompressionLevel {
+		/// <summary>
+		///    The audio is not compressed.
+		/// </summary>
+		None = 0,
+		
+		/// <summary>
+		///    The audio is mildly compressed.
+		/// </summary>
+		Fast = 1000,
+		
+		/// <summary>
+		///    The audio is compressed at a normal level.
+		/// </summary>
+		Normal = 2000,
+		
+		/// <summary>
+		///    The audio is highly compressed.
+		/// </summary>
+		High = 3000,
+		
+		/// <summary>
+		///    The audio is extremely highly compressed.
+		/// </summary>
+		ExtraHigh = 4000,
+		
+		/// <summary>
+		///    The audio is compressed to an insane level.
+		/// </summary>
+		Insane
+	}
+	
+	/// <summary>
+	///    This struct implements <see cref="IAudioCodec" /> to provide
+	///    support for reading Monkey's Audio APE stream properties.
+	/// </summary>
+	public struct StreamHeader : IAudioCodec, ILosslessAudioCodec
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the APE version.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (4,5) of the file and is
+		///    1000 times the actual version number, so 3810 indicates
+		///    version 3.81.
+		/// </remarks>
+		private ushort version;
+		
+		// Ape Header (24 bytes) starting at Offest 52 into the file
+		/// <summary>
+		///    Contains the compression level.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (51,52).
+		/// </remarks>
+		private CompressionLevel compression_level;
+		
+		/*
+		/// <summary>
+		///    Contains the format flags.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (53,54).
+		/// </remarks>
+		private ushort format_flags;
+		*/
+		
+		/// <summary>
+		///    Contains the number of audio blocks in one frame.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (55-58).
+		/// </remarks>
+		private uint blocks_per_frame;
+		
+		/// <summary>
+		///    Contains the number of audio blocks in the final frame.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (59-62).
+		/// </remarks>
+		private uint final_frame_blocks;
+		
+		/// <summary>
+		///    Contains the total number of frames.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (63-66).
+		/// </remarks>
+		private uint total_frames;
+		
+		/// <summary>
+		///    Contains the number of bits per sample.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (67,68) and is typically
+		///    16.
+		/// </remarks>
+		private ushort bits_per_sample;
+		
+		/// <summary>
+		///    Contains the number of channels.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (69,70) and is typically
+		///    1 or 2.
+		/// </remarks>
+		private ushort channels;
+		
+		/// <summary>
+		///    Contains the sample rate.
+		/// </summary>
+		/// <remarks>
+		///    This value is stored in bytes (71-74) and is typically
+		///    44100.
+		/// </remarks>
+		private uint sample_rate;
+		
+		/// <summary>
+		///    Contains the length of the audio stream.
+		/// </summary>
+		/// <remarks>
+		///    This value is provided by the constructor.
+		/// </remarks>
+		private long stream_length;
+		
+		#endregion
+		
+		
+		
+		#region Public Static Fields
+		
+		/// <summary>
+		///    The size of a Monkey Audio header.
+		/// </summary>
+		public const uint Size = 76;
+		
+		/// <summary>
+		///    The identifier used to recognize a WavPack file.
+		/// </summary>
+		/// <value>
+		///    "MAC "
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier =
+			"MAC ";
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StreamHeader" /> for a specified header block and
+		///    stream length.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the stream
+		///    header data.
+		/// </param>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value containing the length of the
+		///    Monkey Audio stream in bytes.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> does not begin with <see
+		///    cref="FileIdentifier" /> or is less than <see cref="Size"
+		///    /> bytes long.
+		/// </exception>
+		public StreamHeader(ByteVector data, long streamLength)
+		{
+			if (data == null)
+				throw new ArgumentNullException("data");
+			
+			if (!data.StartsWith(FileIdentifier))
+				throw new CorruptFileException(
+					"Data does not begin with identifier.");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException(
+					"Insufficient data in stream header");
+			
+			stream_length = streamLength;
+			version = data.Mid (4, 2).ToUShort(false);
+			compression_level = (CompressionLevel) data.Mid(52, 2)
+				.ToUShort(false);
+			// format_flags = data.Mid(54, 2).ToUShort(false);
+			blocks_per_frame = data.Mid(56, 4).ToUInt(false);
+			final_frame_blocks = data.Mid(60, 4).ToUInt(false);
+			total_frames = data.Mid(64, 4).ToUInt(false);
+			bits_per_sample = data.Mid(68, 2).ToUShort(false);
+			channels = data.Mid(70, 2).ToUShort(false);
+			sample_rate = data.Mid(72, 4).ToUInt(false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		public TimeSpan Duration {
+			get {
+				if (sample_rate <= 0 || total_frames <= 0)
+					return TimeSpan.Zero;
+				
+				return TimeSpan.FromSeconds (
+					(double) ((total_frames - 1) *
+					blocks_per_frame + final_frame_blocks) /
+					(double) sample_rate);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {
+				return string.Format(
+					CultureInfo.InvariantCulture,
+					"Monkey's Audio APE Version {0:0.000}",
+					Version);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				TimeSpan d = Duration;
+				if (d <= TimeSpan.Zero)
+					return 0;
+				
+				return (int) ((stream_length * 8L) /
+					d.TotalSeconds) / 1000;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {return (int)sample_rate;}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return channels;}
+		}
+		
+		/// <summary>
+		///    Gets the APE version of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="double" /> value containing the APE version
+		///    of the audio represented by the current instance.
+		/// </value>
+		public double Version {
+			get {return (double) version / (double) 1000;}
+		}
+		
+		/// <summary>
+		///    Gets the number of bits per sample in the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of bits
+		///    per sample in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int BitsPerSample {
+			get {return bits_per_sample;}
+		}
+		
+		/// <summary>
+		///    Gets the level of compression used when encoding the
+		///    audio represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="CompressionLevel" /> value indicating the
+		///    level of compression used when encoding the audio
+		///    represented by the current instance.
+		/// </value>
+		public CompressionLevel Compression {
+			get {return compression_level;}
+		}
+		
+		#endregion
+	}}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Ape/Tag.cs b/lib/TagLib/TagLib/Ape/Tag.cs
new file mode 100644
index 0000000..9865ca4
--- /dev/null
+++ b/lib/TagLib/TagLib/Ape/Tag.cs
@@ -0,0 +1,1563 @@
+//
+// Tag.cs: Provides a representation of an APEv2 tag which can be read from and
+// written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   apetag.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2004 Allan Sandfeld Jensen (Original 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
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace TagLib.Ape {
+	/// <summary>
+	///    This class extends <see cref="TagLib.Tag" /> and implements <see
+	///    cref="T:System.Collections.Generic.IEnumerable`1" /> to provide a representation of an APEv2
+	///    tag which can be read from and written to disk.
+	/// </summary>
+	public class Tag : TagLib.Tag, IEnumerable<string>
+	{
+		
+#region Private Static Fields
+		
+		/// <summary>
+		///    Contains names of picture fields, indexed to correspond
+		///    to their picture item names.
+		/// </summary>
+		private static string [] picture_item_names = new string [] {
+			"Cover Art (other)",
+			"Cover Art (icon)",
+			"Cover Art (other icon)",
+			"Cover Art (front)",
+			"Cover Art (back)",
+			"Cover Art (leaflet)",
+			"Cover Art (media)",
+			"Cover Art (lead)",
+			"Cover Art (artist)",
+			"Cover Art (conductor)",
+			"Cover Art (band)",
+			"Cover Art (composer)",
+			"Cover Art (lyricist)",
+			"Cover Art (studio)",
+			"Cover Art (recording)",
+			"Cover Art (performance)",
+			"Cover Art (movie scene)",
+			"Cover Art (colored fish)",
+			"Cover Art (illustration)",
+			"Cover Art (band logo)",
+			"Cover Art (publisher logo)"
+		};
+		
+#endregion
+		
+		
+		
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the tag footer.
+		/// </summary>
+		private Footer footer = new Footer ();
+		
+		/// <summary>
+		///    Contains the items in the tag.
+		/// </summary>
+		private List<Item> items = new List<Item> ();
+		
+		#endregion
+		
+		
+		
+		#region Public Static Properties
+		
+		/// <summary>
+		///    Specifies the identifier used find an APEv2 tag in a
+		///    file.
+		/// </summary>
+		/// <value>
+		///    "<c>APETAGEX</c>"
+		/// </value>
+		[Obsolete("Use Footer.FileIdentifer")]
+		public static readonly ReadOnlyByteVector FileIdentifier =
+			Footer.FileIdentifier;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> with no contents.
+		/// </summary>
+		public Tag ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> by reading the contents from a specified
+		///    position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    from which the contents of the new instance is to be
+		///    read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the tag.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		public Tag (TagLib.File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (position < 0 ||
+				position > file.Length - Footer.Size)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			Read (file, position);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> by reading the contents of a raw tag in a
+		///    specified <see cref="ByteVector"/> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    tag.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null"/>.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> is too small to contain a tag,
+		///    has a header where the footer should be, or is smaller
+		///    than the tag it is supposed to contain.
+		/// </exception>
+		public Tag (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < Footer.Size)
+				throw new CorruptFileException (
+					"Does not contain enough footer data.");
+			
+			footer = new Footer (
+				data.Mid ((int) (data.Count - Footer.Size)));
+			
+			if (footer.TagSize == 0)
+				throw new CorruptFileException (
+					"Tag size out of bounds.");
+			
+			// If we've read a header at the end of the block, the
+			// block is invalid.
+			if ((footer.Flags & FooterFlags.IsHeader) != 0)
+				throw new CorruptFileException (
+					"Footer was actually header.");
+			
+			if (data.Count < footer.TagSize)
+				throw new CorruptFileException (
+					"Does not contain enough tag data.");
+			
+			Parse (data.Mid ((int) (data.Count - footer.TagSize),
+				(int) (footer.TagSize - Footer.Size)));
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets whether or not the current instance has a
+		///    header when rendered.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance has a header when rendered.
+		/// </value>
+		public bool HeaderPresent {
+			get {
+				return (footer.Flags &
+					FooterFlags.HeaderPresent) != 0;
+			}
+			set {
+				if (value)
+					footer.Flags |= FooterFlags.HeaderPresent;
+				else
+					footer.Flags &= ~FooterFlags.HeaderPresent;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Adds a number to the value stored in a specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to store the value in.
+		/// </param>
+		/// <param name="number">
+		///    A <see cref="uint" /> value containing the number to
+		///    store.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="uint" /> value representing a total which
+		///    <paramref name="number" /> is a part of, or zero if
+		///    <paramref name="number" /> is not part of a set.
+		/// </param>
+		/// <remarks>
+		///    If both <paramref name="number" /> and <paramref
+		///    name="count" /> are equal to zero, the value will not be
+		///    added. If <paramref name="count" /> is zero, <paramref
+		///    name="number" /> by itself will be stored. Otherwise, the
+		///    values will be stored as "<paramref name="number"
+		///    />/<paramref name="count" />".
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void AddValue (string key, uint number, uint count)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (number == 0 && count == 0)
+				return;
+			else if (count != 0)
+				AddValue (key, string.Format (
+					CultureInfo.InvariantCulture, "{0}/{1}",
+					number, count));
+			else
+				AddValue (key, number.ToString (
+					CultureInfo.InvariantCulture));
+		}
+		
+		/// <summary>
+		///    Stores a number in a specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to store the value in.
+		/// </param>
+		/// <param name="number">
+		///    A <see cref="uint" /> value containing the number to
+		///    store.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="uint" /> value representing a total which
+		///    <paramref name="number" /> is a part of, or zero if
+		///    <paramref name="number" /> is not part of a set.
+		/// </param>
+		/// <remarks>
+		///    If both <paramref name="number" /> and <paramref
+		///    name="count" /> are equal to zero, the value will be
+		///    cleared. If <paramref name="count" /> is zero, <paramref
+		///    name="number" /> by itself will be stored. Otherwise, the
+		///    values will be stored as "<paramref name="number"
+		///    />/<paramref name="count" />".
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="ident" /> is <see langword="null" />.
+		/// </exception>
+		public void SetValue (string key, uint number, uint count)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (number == 0 && count == 0)
+				RemoveItem (key);
+			else if (count != 0)
+				SetValue (key, string.Format (
+					CultureInfo.InvariantCulture, "{0}/{1}",
+					number, count));
+			else
+				SetValue (key, number.ToString (
+					CultureInfo.InvariantCulture));
+		}
+		
+		/// <summary>
+		///    Adds the contents of a <see cref="string" /> to the value
+		///    stored in a specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to store the value in.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string" /> object containing the text to
+		///    add.
+		/// </param>
+		/// <remarks>
+		///    If <paramref name="value" /> is <see langword="null" />
+		///    or empty, the value will not be added.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void AddValue (string key, string value)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (string.IsNullOrEmpty (value))
+				return;
+			
+			AddValue (key, new string [] {value});
+		}
+		
+		/// <summary>
+		///    Stores the contents of a <see cref="string" /> in a
+		///    specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to store the value in.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string" /> object containing the text to
+		///    store.
+		/// </param>
+		/// <remarks>
+		///    If <paramref name="value" /> is <see langword="null" />
+		///    or empty, the value will be cleared.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void SetValue (string key, string value)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (string.IsNullOrEmpty (value))
+				RemoveItem (key);
+			else
+				SetValue (key, new string [] {value});
+		}
+		
+		/// <summary>
+		///    Adds the contents of a <see cref="string[]" /> to the
+		///    value stored in a specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to store the value in.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string[]" /> containing the text to add.
+		/// </param>
+		/// <remarks>
+		///    If <paramref name="value" /> is <see langword="null" />
+		///    or empty, the value will not be added.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void AddValue (string key, string [] value)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (value == null || value.Length == 0)
+				return;
+			
+			int index = GetItemIndex (key);
+			
+			List<string> values = new List<string> ();
+			
+			if (index >= 0)
+				values.AddRange (items [index].ToStringArray ());
+			
+			values.AddRange (value);
+			
+			Item item = new Item (key, values.ToArray ());
+			
+			if (index >= 0)
+				items [index] = item;
+			else
+				items.Add (item);
+		}
+		
+		/// <summary>
+		///    Stores the contents of a <see cref="string[]" /> in a
+		///    specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to store the value in.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string[]" /> containing the text to store.
+		/// </param>
+		/// <remarks>
+		///    If <paramref name="value" /> is <see langword="null" />
+		///    or empty, the value will be cleared.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void SetValue (string key, string [] value)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (value == null || value.Length == 0) {
+				RemoveItem (key);
+				return;
+			}
+			
+			Item item = new Item (key, value);
+			
+			int index = GetItemIndex (key);
+			if (index >= 0)
+				items [index] = item;
+			else
+				items.Add (item);
+			
+		}
+		
+		/// <summary>
+		///    Gets a specified item from the current instance.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to get from the current instance.
+		/// </param>
+		/// <returns>
+		///    The item with the matching name contained in the current
+		///    instance, or <see langword="null" /> if a matching object
+		///    was not found.
+		/// </returns>
+		public Item GetItem (string key)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			StringComparison comparison =
+				StringComparison.InvariantCultureIgnoreCase;
+			
+			foreach (Item item in items)
+				if (key.Equals (item.Key, comparison))
+					return item;
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Adds an item to the current instance, replacing the
+		///    existing one of the same name.
+		/// </summary>
+		/// <param name="item">
+		///    A <see cref="Item" /> object to add to the current
+		///    instance.
+		/// </param>
+		public void SetItem (Item item)
+		{
+			if (item == null)
+				throw new ArgumentNullException ("item");
+			
+			int index = GetItemIndex (item.Key);
+			if (index >= 0)
+				items [index] = item;
+			else
+				items.Add (item);
+		}
+		
+		/// <summary>
+		///    Removes the item with a specified key from the current
+		///    instance.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to remove from the current instance.
+		/// </param>
+		public void RemoveItem (string key)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			StringComparison comparison =
+				StringComparison.InvariantCultureIgnoreCase;
+			
+			for (int i = items.Count - 1; i >= 0; i --)
+				if (key.Equals (items [i].Key, comparison))
+					items.RemoveAt (i);
+		}
+		
+		/// <summary>
+		/// Checks if an item exists.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to check.
+		/// </param>
+		/// <returns>
+		///    Returns <see langword="true"/> if the <paramref name="key"/>
+		///    exists - else <see langword="false"/> is returned.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public bool HasItem(string key)
+		{
+			if (key == null)
+				throw new ArgumentNullException("key");
+			
+			return GetItemIndex(key) >= 0;
+		}
+		
+		/// <summary>
+		///    Renders the current instance as a raw APEv2 tag.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered tag.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			uint item_count = 0;
+			
+			foreach (Item item in items) {
+				data.Add (item.Render ());
+				item_count ++;
+			}
+			
+			footer.ItemCount = item_count;
+			footer.TagSize = (uint) (data.Count + Footer.Size);
+			HeaderPresent = true;
+			
+			data.Insert (0, footer.RenderHeader ());
+			data.Add (footer.RenderFooter ());
+			return data;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the current instance be reading in a tag from
+		///    a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the tag from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to read the tag.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than 0 or greater
+		///    than the size of the file.
+		/// </exception>
+		protected void Read (TagLib.File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Mode = File.AccessMode.Read;
+			
+			if (position < 0 || position > file.Length - Footer.Size)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			file.Seek (position);
+			footer = new Footer (file.ReadBlock ((int)Footer.Size));
+			
+			if (footer.TagSize == 0)
+				throw new CorruptFileException (
+					"Tag size out of bounds.");
+			
+			// If we've read a header, we don't have to seek to read
+			// the content. If we've read a footer, we need to move
+			// back to the start of the tag.
+			if ((footer.Flags & FooterFlags.IsHeader) == 0)
+				file.Seek (position + Footer.Size - footer.TagSize);
+			
+			Parse (file.ReadBlock ((int)(footer.TagSize - Footer.Size)));
+		}
+		
+		/// <summary>
+		///    Populates the current instance by parsing the contents of
+		///    a raw APEv2 tag, minus the header and footer.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the content
+		///    of an APEv2 tag, minus the header and footer.
+		/// </param>
+		/// <remarks>
+		///    This method must only be called after the internal
+		///    footer has been read from the file, otherwise the data
+		///    cannot be parsed correctly.
+		/// </remarks>
+		protected void Parse (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			int pos = 0;
+			
+			try {
+				// 11 bytes is the minimum size for an APE item
+				for (uint i = 0; i < footer.ItemCount &&
+					pos <= data.Count - 11; i++) {
+					Item item = new Item (data, pos);
+					SetItem (item);
+					pos += item.Size;
+				}
+			} catch (CorruptFileException) {
+				// A corrupt item was encountered, considered
+				// the tag finished with what has been read.
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Gets the index of an item in the current instance.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key to look
+		///    for in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value containing the index in <see
+		///    cref="items" /> at which the item appears, or -1 if the
+		///    item was not found.
+		/// </returns>
+		/// <remarks>
+		///    Keys are compared in a case insensitive manner.
+		/// </remarks>
+		private int GetItemIndex (string key)
+		{
+			StringComparison comparison =
+				StringComparison.InvariantCultureIgnoreCase;
+			
+			for (int i = 0; i < items.Count; i ++)
+				if (key.Equals (items [i].Key, comparison))
+					return i;
+			
+			return -1;
+		}
+		
+		/// <summary>
+		///    Gets the text value from a specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to get the value from.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the text of the
+		///    specified frame, or <see langword="null" /> if no value
+		///    was found.
+		/// </returns>
+		private string GetItemAsString (string key)
+		{
+				Item item = GetItem (key);
+				return item != null ? item.ToString () : null;
+		}
+		
+		/// <summary>
+		///    Gets the text values from a specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to get the value from.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the text of the
+		///    specified frame, or an empty array if no values were
+		///    found.
+		/// </returns>
+		private string [] GetItemAsStrings (string key)
+		{
+			Item item = GetItem (key);
+			return item != null ?
+				item.ToStringArray () : new string [0];
+		}
+		
+		/// <summary>
+		///    Gets an integer value from a "/" delimited list in a
+		///    specified item.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string" /> object containing the key of the
+		///    item to get the value from.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the index in the
+		///    integer list of the value to return.
+		/// </param>
+		/// <returns>
+		///    A <see cref="uint" /> value read from the list in the
+		///    frame, or 0 if the value wasn't found.
+		/// </returns>
+		private uint GetItemAsUInt32 (string key, int index)
+		{
+			string text = GetItemAsString (key);
+			
+			if (text == null)
+				return 0;
+			
+			string [] values = text.Split (new char [] {'/'},
+				index + 2);
+			
+			if (values.Length < index + 1)
+				return 0;
+			
+			uint result;
+			if (uint.TryParse (values [index], out result))
+				return result;
+			
+			return 0;
+		}
+		
+		#endregion
+		
+		
+		
+		#region IEnumerable
+		
+		/// <summary>
+		///    Gets the enumerator for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerator`1" /> object enumerating through
+		///    the item keys stored in the current instance.
+		/// </returns>
+		public IEnumerator<string> GetEnumerator ()
+		{
+			foreach (Item item in items)
+				yield return item.Key;
+		}
+		
+		/// <summary>
+		///    Gets the enumerator for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="IEnumerator" /> object enumerating through
+		///    the item keys stored in the current instance.
+		/// </returns>
+		IEnumerator IEnumerable.GetEnumerator()
+		{
+			return GetEnumerator ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.Ape" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.Ape;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Title" item.
+		/// </remarks>
+		public override string Title {
+			get { return GetItemAsString ("Title"); }
+			set {SetValue ("Title", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names of the Title of the
+		///    media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names for
+		///    the Title of the media described by the current instance,
+		///    or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TitleSort" item.
+		/// </remarks>
+		public override string TitleSort {
+			get { return GetItemAsString ("TitleSort"); }
+			set { SetValue ("TitleSort", value); }
+		}
+
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Artist" item.
+		/// </remarks>
+		public override string [] Performers {
+			get {return GetItemAsStrings ("Artist");}
+			set {SetValue ("Artist", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> array containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ArtistSort" field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string[] PerformersSort {
+			get { return GetItemAsStrings ("ArtistSort"); }
+			set { SetValue ("ArtistSort", value); }
+		}
+
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Album Artist"
+		///    item, and "AlbumArtist" as a backup property if it exists.
+		/// </remarks>
+		public override string [] AlbumArtists {
+			get {
+				string[] list = GetItemAsStrings("Album Artist");
+				if (list.Length == 0)
+					list = GetItemAsStrings("AlbumArtist");
+				return list;
+			}
+			set {
+				SetValue("Album Artist", value);
+				// compatibility
+				if (HasItem("AlbumArtist"))
+					SetValue("AlbumArtist", value);
+				}
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names for the band or artist who
+		///    is credited in the creation of the entire album or
+		///    collection containing the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> array containing the sort names
+		///    for the band or artist who is credited in the creation
+		///    of the entire album or collection containing the media
+		///    described by the current instance or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "AlbumArtistSort"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string[] AlbumArtistsSort {
+			get { return GetItemAsStrings ("AlbumArtistSort"); }
+			set { SetValue ("AlbumArtistSort", value); }
+		}
+
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Composer" item.
+		/// </remarks>
+		public override string [] Composers {
+			get {return GetItemAsStrings ("Composer");}
+			set {SetValue ("Composer", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names for the composers of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> array containing the sort names
+		///    for the composer of the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ComposerSort"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string[] ComposersSort {
+			get { return GetItemAsStrings ("ComposerSort"); }
+			set { SetValue ("ComposerSort", value); }
+		}
+
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Album" item.
+		/// </remarks>
+		public override string Album {
+			get {return GetItemAsString ("Album");}
+			set {SetValue ("Album", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names for the Album Title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort name of 
+		///    the Album Title of the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "AlbumSort"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string AlbumSort {
+			get { return GetItemAsString ("AlbumSort"); }
+			set { SetValue ("AlbumSort", value); }
+		}
+
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Comment" item.
+		/// </remarks>
+		public override string Comment {
+			get {return GetItemAsString ("Comment");}
+			set {SetValue ("Comment", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Genre" item.
+		/// </remarks>
+		public override string [] Genres {
+			get {return GetItemAsStrings ("Genre");}
+			set {SetValue ("Genre", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Year" item.
+		/// </remarks>
+		public override uint Year {
+			get {
+				string text = GetItemAsString ("Year");
+				
+				if (text == null || text.Length == 0)
+					return 0;
+				
+				uint value;
+				if (uint.TryParse (text, out value) ||
+					(text.Length >= 4 && uint.TryParse (
+						text.Substring (0, 4),
+						out value)))
+					return value;
+				
+				return 0;
+			}
+			set {SetValue ("Year", value, 0);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Track" item.
+		/// </remarks>
+		public override uint Track {
+			get {return GetItemAsUInt32 ("Track", 0);}
+			set {SetValue ("Track", value, TrackCount);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Track" item.
+		/// </remarks>
+		public override uint TrackCount {
+			get {return GetItemAsUInt32 ("Track", 1);}
+			set {SetValue ("Track", Track, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Disc" item.
+		/// </remarks>
+		public override uint Disc {
+			get {return GetItemAsUInt32 ("Disc", 0);}
+			set {SetValue ("Disc", value, DiscCount);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Disc" item.
+		/// </remarks>
+		public override uint DiscCount {
+			get {return GetItemAsUInt32 ("Disc", 1);}
+			set {SetValue ("Disc", Disc, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Lyrics" item.
+		/// </remarks>
+		public override string Lyrics {
+			get {return GetItemAsString ("Lyrics");}
+			set {SetValue ("Lyrics", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Grouping" item.
+		/// </remarks>
+		public override string Grouping {
+			get {return GetItemAsString ("Grouping");}
+			set {SetValue ("Grouping", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "BPM" item.
+		/// </remarks>
+		public override uint BeatsPerMinute {
+			get {
+				string text = GetItemAsString ("BPM");
+				
+				if (text == null)
+					return 0;
+				
+				double value;
+				
+				if (double.TryParse (text, out value))
+					return (uint) Math.Round (value);
+				
+				return 0;
+			}
+			set {SetValue ("BPM", value, 0);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Conductor" item.
+		/// </remarks>
+		public override string Conductor {
+			get {return GetItemAsString ("Conductor");}
+			set {SetValue ("Conductor", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Copyright" item.
+		/// </remarks>
+		public override string Copyright {
+			get {return GetItemAsString ("Copyright");}
+			set {SetValue ("Copyright", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Artist ID of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ArtistID for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ARTISTID" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzArtistId {
+			get {return GetItemAsString ("MUSICBRAINZ_ARTISTID");}
+			set {SetValue ("MUSICBRAINZ_ARTISTID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release ID of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseID for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMID" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseId {
+			get {return GetItemAsString ("MUSICBRAINZ_ALBUMID");}
+			set {SetValue ("MUSICBRAINZ_ALBUMID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Artist ID of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseArtistID for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMARTISTID" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseArtistId {
+			get {return GetItemAsString ("MUSICBRAINZ_ALBUMARTISTID");}
+			set {SetValue ("MUSICBRAINZ_ALBUMARTISTID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Track ID of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    TrackID for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_TRACKID" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzTrackId {
+			get {return GetItemAsString ("MUSICBRAINZ_TRACKID");}
+			set {SetValue ("MUSICBRAINZ_TRACKID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Disc ID of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    DiscID for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_DISCID" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzDiscId {
+			get {return GetItemAsString ("MUSICBRAINZ_DISCID");}
+			set {SetValue ("MUSICBRAINZ_DISCID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicIPPUID
+		///    for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICIP_PUID" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicIpId {
+			get {return GetItemAsString ("MUSICIP_PUID");}
+			set {SetValue ("MUSICIP_PUID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the Amazon ID of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the AmazonID
+		///    for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ASIN" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string AmazonId {
+			get {return GetItemAsString ("ASIN");}
+			set {SetValue ("ASIN", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Status of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseStatus for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMSTATUS" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseStatus {
+			get {return GetItemAsString ("MUSICBRAINZ_ALBUMSTATUS");}
+			set {SetValue ("MUSICBRAINZ_ALBUMSTATUS", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Type of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseType for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMTYPE" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseType {
+			get {return GetItemAsString ("MUSICBRAINZ_ALBUMTYPE");}
+			set {SetValue ("MUSICBRAINZ_ALBUMTYPE", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseCountry of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseCountry for the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "RELEASECOUNTRY" item.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseCountry {
+			get {return GetItemAsString ("RELEASECOUNTRY");}
+			set {SetValue ("RELEASECOUNTRY", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "Cover Art" items
+		///    and supports only one picture per type.
+		/// </remarks>
+		public override IPicture [] Pictures {
+			get {
+				List<IPicture> pictures = new List<IPicture> ();
+				
+				for (int i = 0; i < picture_item_names.Length;
+					i++) {
+					Item item = GetItem (
+						picture_item_names [i]);
+					
+					if (item == null ||
+						item.Type != ItemType.Binary)
+						continue;
+					
+					int index = item.Value.Find (
+						ByteVector.TextDelimiter (
+							StringType.UTF8));
+					
+					if (index < 0)
+						continue;
+					
+					Picture pic = new Picture (
+						item.Value.Mid (index + 1));
+					
+					pic.Description = item.Value
+						.ToString (StringType.UTF8, 0,
+							index);
+					
+					pic.Type = (PictureType) i;
+					
+					pictures.Add (pic);
+				}
+				
+				return pictures.ToArray ();
+			}
+			set {
+				foreach (string item_name in picture_item_names)
+					RemoveItem (item_name);
+				
+				if (value == null || value.Length == 0)
+					return;
+				
+				foreach (IPicture pic in value) {
+					int type = (int) pic.Type;
+					
+					if (type >= picture_item_names.Length)
+						type = 0;
+					
+					string name = picture_item_names [type];
+					
+					if (GetItem (name) != null)
+						continue;
+					
+					ByteVector data = ByteVector
+						.FromString (
+							pic.Description,
+							StringType.UTF8);
+					data.Add (ByteVector.TextDelimiter (
+						StringType.UTF8));
+					data.Add (pic.Data);
+					
+					SetItem (new Item (name, data));
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance does not
+		///    any values. Otherwise <see langword="false" />.
+		/// </value>
+		public override bool IsEmpty {
+			get {return items.Count == 0;}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			items.Clear ();
+		}
+		
+		/// <summary>
+		///    Copies the values from the current instance to another
+		///    <see cref="TagLib.Tag" />, optionally overwriting
+		///    existing values.
+		/// </summary>
+		/// <param name="target">
+		///    A <see cref="TagLib.Tag" /> object containing the target
+		///    tag to copy values to.
+		/// </param>
+		/// <param name="overwrite">
+		///    A <see cref="bool" /> specifying whether or not to copy
+		///    values over existing one.
+		/// </param>
+		/// <remarks>
+		///    <para>If <paramref name="target" /> is of type <see
+		///    cref="TagLib.Ape.Tag" /> a complete copy of all values
+		///    will be performed. Otherwise, only standard values will
+		///    be copied.</para>
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="target" /> is <see langword="null" />.
+		/// </exception>
+		public override void CopyTo (TagLib.Tag target, bool overwrite)
+		{
+			if (target == null)
+				throw new ArgumentNullException ("target");
+			
+			TagLib.Ape.Tag match = target as TagLib.Ape.Tag;
+			
+			if (match == null) {
+				base.CopyTo (target, overwrite);
+				return;
+			}
+			
+			foreach (Item item in items) {
+				if (!overwrite &&
+					match.GetItem (item.Key) != null)
+					continue;
+				
+				match.items.Add (item.Clone ());
+			}
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/ContentDescriptionObject.cs b/lib/TagLib/TagLib/Asf/ContentDescriptionObject.cs
new file mode 100644
index 0000000..7f3200e
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/ContentDescriptionObject.cs
@@ -0,0 +1,270 @@
+//
+// ContentDescriptionObject.cs: Provides a representation of an ASF Content
+// Description object which can be read from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF Content Description object which can be
+	///    read from and written to disk.
+	/// </summary>
+	public class ContentDescriptionObject : Object
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the media title.
+		/// </summary>
+		private string title = string.Empty;
+		
+		/// <summary>
+		///    Contains the author/performer.
+		/// </summary>
+		private string author = string.Empty;
+		
+		/// <summary>
+		///    Contains the copyright information.
+		/// </summary>
+		private string copyright = string.Empty;
+		
+		/// <summary>
+		///    Contains the description of the media.
+		/// </summary>
+		private string description = string.Empty;
+		
+		/// <summary>
+		///    Contains the rating of the media.
+		/// </summary>
+		private string rating = string.Empty;
+		
+		#endregion
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptionObject" /> by reading the
+		///    contents from a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or smaller than the minimum size.
+		/// </exception>
+		public ContentDescriptionObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			if (Guid != Asf.Guid.AsfContentDescriptionObject)
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (OriginalSize < 34)
+				throw new CorruptFileException (
+					"Object size too small.");
+			
+			ushort title_length = file.ReadWord ();
+			ushort author_length = file.ReadWord ();
+			ushort copyright_length = file.ReadWord ();
+			ushort description_length = file.ReadWord ();
+			ushort rating_length = file.ReadWord ();
+			
+			title = file.ReadUnicode (title_length);
+			author = file.ReadUnicode (author_length);
+			copyright = file.ReadUnicode (copyright_length);
+			description = file.ReadUnicode (description_length);
+			rating = file.ReadUnicode (rating_length);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptionObject" /> with no contents.
+		/// </summary>
+		public ContentDescriptionObject ()
+			: base (Asf.Guid.AsfContentDescriptionObject)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Region
+		
+		/// <summary>
+		///    Gets and sets the title of the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title of
+		///    the media or <see langword="null" /> if it is not set.
+		/// </value>
+		public string Title {
+			get {return title.Length == 0 ? null : title;}
+			set {
+				title = string.IsNullOrEmpty (value) ?
+					string.Empty : value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the author or performer of the media
+		///    described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the author of
+		///    the media or <see langword="null" /> if it is not set.
+		/// </value>
+		public string Author {
+			get {return author.Length == 0 ? null : author;}
+			set {
+				author = string.IsNullOrEmpty (value) ?
+					string.Empty : value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media or <see langword="null" /> if
+		///    it is not set.
+		/// </value>
+		public string Copyright {
+			get {return copyright.Length == 0 ? null : copyright;}
+			set {
+				copyright = string.IsNullOrEmpty (value) ?
+					string.Empty : value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the description of the media described by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media or <see langword="null" /> if it is not set.
+		/// </value>
+		public string Description {
+			get {
+				return description.Length == 0 ?
+					null : description;
+			}
+			set {
+				description = string.IsNullOrEmpty (value) ?
+					string.Empty : value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the rating of the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a rating of the
+		///    media or <see langword="null" /> if it is not set.
+		/// </value>
+		public string Rating {
+			get {return rating.Length == 0 ? null : rating;}
+			set {
+				rating = string.IsNullOrEmpty (value) ?
+					string.Empty : value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if all the values are cleared.
+		///    Otherwise <see langword="false" />.
+		/// </value>
+		public bool IsEmpty {
+			get {
+				return title.Length == 0 &&
+				author.Length == 0 &&
+				copyright.Length == 0 &&
+				description.Length == 0 &&
+				rating.Length == 0;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Region
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			ByteVector title_bytes = RenderUnicode (title);
+			ByteVector author_bytes = RenderUnicode (author);
+			ByteVector copyright_bytes = RenderUnicode (copyright);
+			ByteVector description_bytes =
+				RenderUnicode (description);
+			ByteVector rating_bytes = RenderUnicode (rating);
+			
+			ByteVector output = RenderWord ((ushort)
+				title_bytes.Count);
+			output.Add (RenderWord ((ushort) author_bytes.Count));
+			output.Add (RenderWord ((ushort) copyright_bytes.Count));
+			output.Add (RenderWord ((ushort)
+				description_bytes.Count));
+			output.Add (RenderWord ((ushort) rating_bytes.Count));
+			output.Add (title_bytes);
+			output.Add (author_bytes);
+			output.Add (copyright_bytes);
+			output.Add (description_bytes);
+			output.Add (rating_bytes);
+			
+			return Render (output);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/ContentDescriptor.cs b/lib/TagLib/TagLib/Asf/ContentDescriptor.cs
new file mode 100644
index 0000000..2ce9967
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/ContentDescriptor.cs
@@ -0,0 +1,494 @@
+//
+// ContentDescriptor.cs: Provides a representation of an ASF Content Descriptor
+// to be used in combination with ExtendedContentDescriptionObject.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    Indicates the type of data stored in a <see
+	///    cref="ContentDescriptor" /> or <see cref="DescriptionRecord" />
+	///    object.
+	/// </summary>
+	public enum DataType {
+		/// <summary>
+		///    The descriptor contains Unicode (UTF-16LE) text.
+		/// </summary>
+		Unicode = 0,
+		
+		/// <summary>
+		///    The descriptor contains binary data.
+		/// </summary>
+		Bytes = 1,
+		
+		/// <summary>
+		///    The descriptor contains a boolean value.
+		/// </summary>
+		Bool = 2,
+		
+		/// <summary>
+		///    The descriptor contains a 4-byte DWORD value.
+		/// </summary>
+		DWord = 3,
+		
+		/// <summary>
+		///    The descriptor contains a 8-byte QWORD value.
+		/// </summary>
+		QWord = 4,
+		
+		/// <summary>
+		///    The descriptor contains a 2-byte WORD value.
+		/// </summary>
+		Word = 5,
+		
+		/// <summary>
+		///    The descriptor contains a 16-byte GUID value.
+		/// </summary>
+		Guid = 6
+	}
+	
+	/// <summary>
+	///    This class provides a representation of an ASF Content
+	///    Descriptor to be used in combination with <see
+	///    cref="ExtendedContentDescriptionObject" />.
+	/// </summary>
+	public class ContentDescriptor
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the data type.
+		/// </summary>
+		private DataType type = DataType.Unicode;
+		
+		/// <summary>
+		///    Contains the descriptor name.
+		/// </summary>
+		private string name = null;
+		
+		/// <summary>
+		///    Contains the string value.
+		/// </summary>
+		private string strValue = null;
+		
+		/// <summary>
+		///    Contains the byte value.
+		/// </summary>
+		private ByteVector byteValue = null;
+		
+		/// <summary>
+		///    Contains the long value.
+		/// </summary>
+		private ulong longValue = 0;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptor" /> with a specified name and
+		///    and value.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string" /> object containing the value for
+		///    the new instance.
+		/// </param>
+		public ContentDescriptor (string name, string value)
+		{
+			this.name = name;
+			this.strValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptor" /> with a specified name and
+		///    and value.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ByteVector" /> object containing the value
+		///    for the new instance.
+		/// </param>
+		public ContentDescriptor (string name, ByteVector value)
+		{
+			this.name = name;
+			this.type = DataType.Bytes;
+			this.byteValue = new ByteVector (value);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptor" /> with a specified name and
+		///    and value.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="uint" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public ContentDescriptor (string name, uint value)
+		{
+			this.name = name;
+			this.type = DataType.DWord;
+			this.longValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptor" /> with a specified name and
+		///    and value.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ulong" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public ContentDescriptor (string name, ulong value)
+		{
+			this.name = name;
+			this.type = DataType.QWord;
+			this.longValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptor" /> with a specified name and
+		///    and value.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ushort" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public ContentDescriptor (string name, ushort value)
+		{
+			this.name = name;
+			this.type = DataType.Word;
+			this.longValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptor" /> with a specified name and
+		///    and value.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="bool" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public ContentDescriptor (string name, bool value)
+		{
+			this.name = name;
+			this.type = DataType.Bool;
+			this.longValue = value ? 1uL : 0;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ContentDescriptor" /> by reading its contents from
+		///    a file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object to read the raw ASF
+		///    Description Record from.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    A valid descriptor could not be read.
+		/// </exception>
+		/// <remarks>
+		///    <paramref name="file" /> must be at a seek position at
+		///    which the descriptor can be read.
+		/// </remarks>
+		protected internal ContentDescriptor (Asf.File file)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (!Parse (file))
+				throw new CorruptFileException (
+					"Failed to parse content descriptor.");
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the name of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the name of the
+		///    current instance.
+		/// </value>
+		public string Name {
+			get {return name;}
+		}
+		
+		/// <summary>
+		///    Gets the type of data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="DataType" /> value indicating type of data
+		///    contained in the current instance.
+		/// </value>
+		public DataType Type {
+			get {return type;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> object containing the value of
+		///    the current instance.
+		/// </returns>
+		public override string ToString ()
+		{
+			if (type == DataType.Unicode)
+				return strValue;
+			
+			if (type == DataType.Bytes)
+				return byteValue.ToString (StringType.UTF16LE);
+			
+			return longValue.ToString ();
+		}
+		
+		/// <summary>
+		///    Gets the binary contents of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    contents of the current instance, or <see langword="null"
+		///    /> if <see cref="Type" /> is unequal to <see
+		///    cref="DataType.Bytes" />.
+		/// </returns>
+		public ByteVector ToByteVector ()
+		{
+			return byteValue;
+		}
+		
+		/// <summary>
+		///    Gets the boolean value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="bool" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public bool ToBool ()
+		{
+			return longValue != 0;
+		}
+		
+		/// <summary>
+		///    Gets the DWORD value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public uint ToDWord ()
+		{
+			uint value;
+			if (type == DataType.Unicode && strValue != null &&
+				uint.TryParse (strValue, out value))
+				return value;
+			
+			return (uint) longValue;
+		}
+		
+		/// <summary>
+		///    Gets the QWORD value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ulong" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public ulong ToQWord ()
+		{
+			ulong value;
+			if (type == DataType.Unicode && strValue != null &&
+				ulong.TryParse (strValue, out value))
+				return value;
+			
+			return longValue;
+		}
+		
+		/// <summary>
+		///    Gets the WORD value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public ushort ToWord ()
+		{
+			ushort value;
+			if (type == DataType.Unicode && strValue != null &&
+				ushort.TryParse (strValue, out value))
+				return value;
+			
+			return (ushort) longValue;
+		}
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF Description
+		///    Record.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector value = null;
+			
+			switch (type)
+			{
+			case DataType.Unicode:
+				value = Object.RenderUnicode (strValue);
+				break;
+			case DataType.Bytes:
+				value = byteValue;
+				break;
+			case DataType.Bool:
+			case DataType.DWord:
+				value = Object.RenderDWord ((uint) longValue);
+				break;
+			case DataType.QWord:
+				value = Object.RenderQWord (longValue);
+				break;
+			case DataType.Word:
+				value = Object.RenderWord ((ushort) longValue);
+				break;
+			default:
+				return null;
+			}
+			
+			ByteVector name = Object.RenderUnicode (this.name);
+			
+			ByteVector output = new ByteVector ();
+			output.Add (Object.RenderWord ((ushort) name.Count));
+			output.Add (name);
+			output.Add (Object.RenderWord ((ushort) type));
+			output.Add (Object.RenderWord ((ushort) value.Count));
+			output.Add (value);
+			
+			return output;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the current instance by reading in the contents
+		///    from a file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object to read the raw ASF
+		///    Content Descriptor from.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the data was read correctly.
+		///    Otherwise <see langword="false" />.
+		/// </returns>
+		protected bool Parse (Asf.File file)
+		{
+			int name_count = file.ReadWord ();
+			name = file.ReadUnicode (name_count);
+			
+			type = (DataType) file.ReadWord ();
+			
+			int value_count = file.ReadWord ();
+			switch (type)
+			{
+			case DataType.Word:
+				longValue = file.ReadWord ();
+				break;
+				
+			case DataType.Bool:
+				longValue = file.ReadDWord ();
+				break;
+				
+			case DataType.DWord:
+				longValue = file.ReadDWord ();
+				break;
+				
+			case DataType.QWord:
+				longValue = file.ReadQWord ();
+				break;
+				
+			case DataType.Unicode:
+				strValue = file.ReadUnicode (value_count);
+				break;
+				
+			case DataType.Bytes:
+				byteValue = file.ReadBlock (value_count);
+				break;
+				
+			default:
+				return false;
+			}
+			
+			return true;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/DescriptionRecord.cs b/lib/TagLib/TagLib/Asf/DescriptionRecord.cs
new file mode 100644
index 0000000..1ba7a3e
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/DescriptionRecord.cs
@@ -0,0 +1,617 @@
+//
+// DescriptionRecord.cs: Provides a representation of an ASF Description Record
+// to be used in combination with MetadataLibaryObject.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class provides a representation of an ASF Description Record
+	///    to be used in combination with <see cref="MetadataLibraryObject"
+	///    />.
+	/// </summary>
+	public class DescriptionRecord
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the data type.
+		/// </summary>
+		private DataType type = DataType.Unicode;
+		
+		/// <summary>
+		///    Contains the language list index.
+		/// </summary>
+		private ushort lang_list_index = 0;
+		
+		/// <summary>
+		///    Contains the stream number.
+		/// </summary>
+		private ushort stream_number = 0;
+		
+		/// <summary>
+		///    Contains the record name.
+		/// </summary>
+		private string name = null;
+		
+		/// <summary>
+		///    Contains the string value.
+		/// </summary>
+		private string strValue = null;
+		
+		/// <summary>
+		///    Contains the byte value.
+		/// </summary>
+		private ByteVector byteValue = null;
+		
+		/// <summary>
+		///    Contains the long value.
+		/// </summary>
+		private ulong longValue = 0;
+		
+		/// <summary>
+		///    Contains the GUID value.
+		/// </summary>
+		private System.Guid guidValue = System.Guid.Empty;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> with a specified language,
+		///    stream, name, and value.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the new instance.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string" /> object containing the value for
+		///    the new instance.
+		/// </param>
+		public DescriptionRecord (ushort languageListIndex,
+		                          ushort streamNumber, string name,
+		                          string value)
+		{
+			this.lang_list_index = languageListIndex;
+			this.stream_number = streamNumber;
+			this.name = name;
+			this.strValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> with a specified language,
+		///    stream, name, and value.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the new instance.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ByteVector" /> object containing the value
+		///    for the new instance.
+		/// </param>
+		public DescriptionRecord (ushort languageListIndex,
+		                          ushort streamNumber, string name,
+		                          ByteVector value)
+		{
+			this.lang_list_index = languageListIndex;
+			this.stream_number = streamNumber;
+			this.name = name;
+			this.type = DataType.Bytes;
+			this.byteValue = new ByteVector (value);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> with a specified language,
+		///    stream, name, and value.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the new instance.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="uint" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public DescriptionRecord (ushort languageListIndex,
+		                          ushort streamNumber, string name,
+		                          uint value)
+		{
+			this.lang_list_index = languageListIndex;
+			this.stream_number = streamNumber;
+			this.name = name;
+			this.type = DataType.DWord;
+			this.longValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> with a specified language,
+		///    stream, name, and value.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the new instance.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ulong" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public DescriptionRecord (ushort languageListIndex,
+		                          ushort streamNumber, string name,
+		                          ulong value)
+		{
+			this.lang_list_index = languageListIndex;
+			this.stream_number = streamNumber;
+			this.name = name;
+			this.type = DataType.QWord;
+			this.longValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> with a specified language,
+		///    stream, name, and value.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the new instance.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ushort" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public DescriptionRecord (ushort languageListIndex,
+		                          ushort streamNumber, string name,
+		                          ushort value)
+		{
+			this.lang_list_index = languageListIndex;
+			this.stream_number = streamNumber;
+			this.name = name;
+			this.type = DataType.Word;
+			this.longValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> with a specified language,
+		///    stream, name, and value.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the new instance.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="bool" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public DescriptionRecord (ushort languageListIndex,
+		                          ushort streamNumber, string name,
+		                          bool value)
+		{
+			this.lang_list_index = languageListIndex;
+			this.stream_number = streamNumber;
+			this.name = name;
+			this.type = DataType.Bool;
+			this.longValue = value ? 1uL : 0;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> with a specified language,
+		///    stream, name, and value.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the new instance.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.Guid" /> value containing the value
+		///    for the new instance.
+		/// </param>
+		public DescriptionRecord (ushort languageListIndex,
+		                          ushort streamNumber, string name,
+		                          System.Guid value)
+		{
+			this.lang_list_index = languageListIndex;
+			this.stream_number = streamNumber;
+			this.name = name;
+			this.type = DataType.Guid;
+			this.guidValue = value;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DescriptionRecord" /> by reading its contents from
+		///    a file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object to read the raw ASF
+		///    Description Record from.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    A valid record could not be read.
+		/// </exception>
+		/// <remarks>
+		///    <paramref name="file" /> must be at a seek position at
+		///    which the record can be read.
+		/// </remarks>
+		protected internal DescriptionRecord (Asf.File file)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (!Parse (file))
+				throw new CorruptFileException (
+					"Failed to parse description record.");
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the index of the language associated with the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value containing the index of the
+		///    language associated with the current instance.
+		/// </value>
+		public ushort LanguageListIndex {
+			get {return lang_list_index;}
+		}
+		
+		/// <summary>
+		///    Gets the index of the stream associated with the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value containing the index of the
+		///    stream associated with the current instance.
+		/// </value>
+		public ushort StreamNumber {
+			get {return stream_number;}
+		}
+		
+		/// <summary>
+		///    Gets the name of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the name of the
+		///    current instance.
+		/// </value>
+		public string Name {
+			get {return name;}
+		}
+		
+		/// <summary>
+		///    Gets the type of data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="DataType" /> value indicating type of data
+		///    contained in the current instance.
+		/// </value>
+		public DataType Type {
+			get {return type;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> object containing the value of
+		///    the current instance.
+		/// </returns>
+		public override string ToString ()
+		{
+			if (type == DataType.Unicode)
+				return strValue;
+			
+			if (type == DataType.Bytes)
+				return byteValue.ToString (StringType.UTF16LE);
+			
+			return longValue.ToString ();
+		}
+		
+		/// <summary>
+		///    Gets the binary contents of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    contents of the current instance, or <see langword="null"
+		///    /> if <see cref="Type" /> is unequal to <see
+		///    cref="DataType.Bytes" />.
+		/// </returns>
+		public ByteVector ToByteVector ()
+		{
+			return byteValue;
+		}
+		
+		/// <summary>
+		///    Gets the boolean value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="bool" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public bool ToBool ()
+		{
+			return longValue != 0;
+		}
+		
+		/// <summary>
+		///    Gets the DWORD value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public uint ToDWord ()
+		{
+			uint value;
+			if (type == DataType.Unicode && strValue != null &&
+				uint.TryParse (strValue, out value))
+				return value;
+			
+			return (uint) longValue;
+		}
+		
+		/// <summary>
+		///    Gets the QWORD value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ulong" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public ulong ToQWord ()
+		{
+			ulong value;
+			if (type == DataType.Unicode && strValue != null &&
+				ulong.TryParse (strValue, out value))
+				return value;
+			
+			return longValue;
+		}
+		
+		/// <summary>
+		///    Gets the WORD value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort" /> value containing the value of the
+		///    current instance.
+		/// </returns>
+		public ushort ToWord ()
+		{
+			ushort value;
+			if (type == DataType.Unicode && strValue != null &&
+				ushort.TryParse (strValue, out value))
+				return value;
+			
+			return (ushort) longValue;
+		}
+		
+		/// <summary>
+		///    Gets the GUID value contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.Guid" /> value containing the value
+		///    of the current instance.
+		/// </returns>
+		public System.Guid ToGuid ()
+		{
+			return guidValue;
+		}
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF Description
+		///    Record.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector value = null;
+			
+			switch (type)
+			{
+			case DataType.Unicode:
+				value = Object.RenderUnicode (strValue);
+				break;
+			case DataType.Bytes:
+				value = byteValue;
+				break;
+			case DataType.Bool:
+			case DataType.DWord:
+				value = Object.RenderDWord ((uint) longValue);
+				break;
+			case DataType.QWord:
+				value = Object.RenderQWord (longValue);
+				break;
+			case DataType.Word:
+				value = Object.RenderWord ((ushort) longValue);
+				break;
+			case DataType.Guid:
+				value = guidValue.ToByteArray ();
+				break;
+			default:
+				return null;
+			}
+			
+			ByteVector name = Object.RenderUnicode (this.name);
+			
+			ByteVector output = new ByteVector ();
+			output.Add (Object.RenderWord (lang_list_index));
+			output.Add (Object.RenderWord (stream_number));
+			output.Add (Object.RenderWord ((ushort) name.Count));
+			output.Add (Object.RenderWord ((ushort) type));
+			output.Add (Object.RenderDWord ((uint) value.Count));
+			output.Add (name);
+			output.Add (value);
+			
+			return output;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the current instance by reading in the contents
+		///    from a file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object to read the raw ASF
+		///    Description Record from.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the data was read correctly.
+		///    Otherwise <see langword="false" />.
+		/// </returns>
+		protected bool Parse (Asf.File file)
+		{
+			// Field name          Field type Size (bits)
+			// Language List Index WORD       16
+			// Stream Number       WORD       16
+			// Name Length         WORD       16
+			// Data Type           WORD       16
+			// Data Length         DWORD      32
+			// Name                WCHAR      varies
+			// Data                See below  varies
+			
+			lang_list_index = file.ReadWord ();
+			stream_number = file.ReadWord ();
+			ushort name_length = file.ReadWord ();
+			type = (DataType) file.ReadWord ();
+			int data_length = (int) file.ReadDWord ();
+			name = file.ReadUnicode (name_length);
+			
+			switch (type)
+			{
+			case DataType.Word:
+				longValue = file.ReadWord ();
+				break;
+			case DataType.Bool:
+			case DataType.DWord:
+				longValue = file.ReadDWord ();
+				break;
+			case DataType.QWord:
+				longValue = file.ReadQWord ();
+				break;
+			case DataType.Unicode:
+				strValue = file.ReadUnicode (data_length);
+				break;
+			case DataType.Bytes:
+				byteValue = file.ReadBlock (data_length);
+				break;
+			case DataType.Guid:
+				guidValue = file.ReadGuid ();
+				break;
+			default:
+				return false;
+			}
+			
+			return true;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/ExtendedContentDescriptionObject.cs b/lib/TagLib/TagLib/Asf/ExtendedContentDescriptionObject.cs
new file mode 100644
index 0000000..9d0f351
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/ExtendedContentDescriptionObject.cs
@@ -0,0 +1,276 @@
+//
+// ExtendedContentDescriptionObject.cs: Provides a representation of an ASF
+// Extended Content Description object which can be read from and written to
+// disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF Extended Content Description object
+	///    which can be read from and written to disk.
+	/// </summary>
+	public class ExtendedContentDescriptionObject : Object,
+		IEnumerable<ContentDescriptor>
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the content descriptors.
+		/// </summary>
+		private List<ContentDescriptor> descriptors =
+			new List<ContentDescriptor> ();
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ExtendedContentDescriptionObject" /> by reading the
+		///    contents from a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or smaller than the minimum size.
+		/// </exception>
+		public ExtendedContentDescriptionObject (Asf.File file,
+		                                         long position)
+			: base (file, position)
+		{
+			if (!Guid.Equals (
+				Asf.Guid.AsfExtendedContentDescriptionObject))
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (OriginalSize < 26)
+				throw new CorruptFileException (
+					"Object size too small.");
+			
+			ushort count = file.ReadWord ();
+			
+			for (ushort i = 0; i < count; i ++)
+				AddDescriptor (new ContentDescriptor (file));
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ExtendedContentDescriptionObject" /> with no
+		///    contents.
+		/// </summary>
+		public ExtendedContentDescriptionObject ()
+			: base (Asf.Guid.AsfExtendedContentDescriptionObject)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance doesn't
+		///    contain any <see cref="ContentDescriptor" /> objects.
+		///    Otherwise <see langword="false" />.
+		/// </value>
+		public bool IsEmpty {
+			get {return descriptors.Count == 0;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			ByteVector output = new ByteVector ();
+			ushort count = 0;
+			
+			foreach (ContentDescriptor desc in descriptors) {
+				count ++;
+				output.Add (desc.Render ());
+			}
+			
+			return Render (RenderWord (count) + output);
+		}
+		
+		/// <summary>
+		///    Removes all descriptors with a given name from the
+		///    current instance.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    descriptors to be removed.
+		/// </param>
+		public void RemoveDescriptors (string name)
+		{
+			for (int i = descriptors.Count - 1; i >= 0; i --)
+				if (name == descriptors [i].Name)
+					descriptors.RemoveAt (i);
+		}
+		
+		/// <summary>
+		///    Gets all descriptors with any of a collection of names
+		///    from the current instance.
+		/// </summary>
+		/// <param name="names">
+		///    A <see cref="string[]" /> containing the names of the
+		///    descriptors to be retrieved.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="names" /> is <see langword="null" />.
+		/// </exception>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the <see cref="ContentDescriptor" /> objects
+		///    retrieved from the current instance.
+		/// </returns>
+		public IEnumerable<ContentDescriptor> GetDescriptors (params string [] names)
+		{
+			if (names == null)
+				throw new ArgumentNullException ("names");
+			
+			foreach (string name in names)
+				foreach (ContentDescriptor desc in descriptors)
+					if (desc.Name == name)
+						yield return desc;
+		}
+		
+		/// <summary>
+		///    Adds a descriptor to the current instance.
+		/// </summary>
+		/// <param name="descriptor">
+		///    A <see cref="ContentDescriptor" /> object to add to the
+		///    current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="descriptor" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public void AddDescriptor (ContentDescriptor descriptor)
+		{
+			if (descriptor == null)
+				throw new ArgumentNullException ("descriptor");
+			
+			descriptors.Add (descriptor);
+		}
+		
+		/// <summary>
+		///    Sets the a collection of desciptors for a given name,
+		///    removing the existing matching records.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    descriptors to be added.
+		/// </param>
+		/// <param name="descriptors">
+		///    A <see cref="ContentDescriptor[]" /> containing
+		///    descriptors to add to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="name" /> is <see langword="null" />.
+		/// </exception>
+		/// <remarks>
+		///    All added entries in <paramref name="descriptors" />
+		///    should match <paramref name="name" /> but it is not
+		///    verified by the method. The descriptors will be added
+		///    with their own names and not the one provided in this
+		///    method, which are used for removing existing values and
+		///    determining where to position the new objects.
+		/// </remarks>
+		public void SetDescriptors (string name,
+		                            params ContentDescriptor [] descriptors)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+			
+			int position = this.descriptors.Count;
+			for (int i = this.descriptors.Count - 1; i >= 0; i --) {
+				if (name == this.descriptors [i].Name) {
+					this.descriptors.RemoveAt (i);
+					position = i;
+				}
+			}
+			this.descriptors.InsertRange (position, descriptors);
+		}
+		
+		#endregion
+		
+		
+		
+#region IEnumerable
+		
+		/// <summary>
+		///    Gets an enumerator for enumerating through the content
+		///    descriptors.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the content descriptors.
+		/// </returns>
+		public IEnumerator<ContentDescriptor> GetEnumerator ()
+		{
+			return descriptors.GetEnumerator ();
+		}
+		
+		System.Collections.IEnumerator
+			System.Collections.IEnumerable.GetEnumerator ()
+		{
+			return descriptors.GetEnumerator ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/File.cs b/lib/TagLib/TagLib/Asf/File.cs
new file mode 100644
index 0000000..a51d99d
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/File.cs
@@ -0,0 +1,431 @@
+//
+// File.cs: Provides tagging and properties support for Microsoft's ASF files.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide tagging
+	///    and properties support for Microsoft's ASF files.
+	/// </summary>
+	[SupportedMimeType("taglib/wma", "wma")]
+	[SupportedMimeType("taglib/wmv", "wmv")]
+	[SupportedMimeType("taglib/asf", "asf")]
+	[SupportedMimeType("audio/x-ms-wma")]
+	[SupportedMimeType("audio/x-ms-asf")]
+	[SupportedMimeType("video/x-ms-asf")]
+	public class File : TagLib.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the file's tag.
+		/// </summary>
+		private Asf.Tag asf_tag = null;
+		
+		/// <summary>
+		///    Contains the file's properties.
+		/// </summary>
+		private Properties properties = null;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path)
+		{
+			Read (propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : this (path, ReadStyle.Average)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle) : base (abstraction)
+		{
+			Read (propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: this (abstraction, ReadStyle.Average)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override TagLib.Tag Tag {
+			get {return asf_tag;}
+		}
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties {
+			get {return properties;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			Mode = AccessMode.Write;
+			try {
+				HeaderObject header = new HeaderObject (this, 0);
+				
+				if (asf_tag == null) {
+					header.RemoveContentDescriptors ();
+					TagTypesOnDisk &= ~ TagTypes.Asf;
+				} else {
+					TagTypesOnDisk |= TagTypes.Asf;
+					header.AddUniqueObject (
+						asf_tag.ContentDescriptionObject);
+					header.AddUniqueObject (
+						asf_tag.ExtendedContentDescriptionObject);
+					header.Extension.AddUniqueObject (
+						asf_tag.MetadataLibraryObject);
+				}
+				
+				ByteVector output = header.Render ();
+				long diff = output.Count - (long) header.OriginalSize;
+				Insert (output, 0, (long) header.OriginalSize);
+				
+				InvariantStartPosition += diff;
+				InvariantEndPosition += diff;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			if (type == TagTypes.Asf)
+				return asf_tag;
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags (TagTypes types)
+		{
+			if ((types & TagTypes.Asf) == TagTypes.Asf)
+				asf_tag.Clear ();
+		}
+		
+		/// <summary>
+		///    Reads a 2-byte WORD from the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort" /> value containing the WORD read
+		///    from the current instance.
+		/// </returns>
+		public ushort ReadWord ()
+		{
+			return ReadBlock (2).ToUShort (false);
+		}
+		
+		/// <summary>
+		///    Reads a 4-byte DWORD from the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the DWORD read
+		///    from the current instance.
+		/// </returns>
+		public uint ReadDWord ()
+		{
+			return ReadBlock (4).ToUInt (false);
+		}
+		
+		/// <summary>
+		///    Reads a 8-byte QWORD from the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ulong" /> value containing the QWORD read
+		///    from the current instance.
+		/// </returns>
+		public ulong ReadQWord ()
+		{
+			return ReadBlock (8).ToULong (false);
+		}
+		
+		/// <summary>
+		///    Reads a 16-byte GUID from the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.Guid" /> value containing the GUID
+		///    read from the current instance.
+		/// </returns>
+		public System.Guid ReadGuid ()
+		{
+			return new System.Guid (ReadBlock (16).Data);
+		}
+		
+		/// <summary>
+		///    Reads a Unicode (UTF-16LE) string of specified length
+		///    from the current instance.
+		/// </summary>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to read. This should always be an even number.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the Unicode
+		///    string read from the current instance.
+		/// </returns>
+		public string ReadUnicode (int length)
+		{
+			ByteVector data = ReadBlock (length);
+			string output = data.ToString (StringType.UTF16LE);
+			int i = output.IndexOf ('\0');
+			return (i >= 0) ? output.Substring (0, i) : output;
+		}
+		
+		/// <summary>
+		///    Reads a collection of objects from the current instance.
+		/// </summary>
+		/// <param name="count">
+		///    A <see cref="uint" /> value specifying the number of
+		///    objects to read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to start reading.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the <see cref="Object" /> objects read from the
+		///    current instance.
+		/// </returns>
+		public IEnumerable<Object> ReadObjects (uint count,
+		                                        long position)
+		{
+			for (int i = 0; i < (int) count; i ++) {
+				Object obj = ReadObject (position);
+				position += (long) obj.OriginalSize;
+				yield return obj;
+			}
+		}
+		
+		/// <summary>
+		///    Reads a <see cref="Object" /> from the current instance.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to start reading.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="Object" /> object of appropriate type as
+		///    read from the current instance.
+		/// </returns>
+		public Object ReadObject (long position)
+		{
+			Seek (position);
+			System.Guid id = ReadGuid ();
+			
+			if (id.Equals (Guid.AsfFilePropertiesObject))
+				return new FilePropertiesObject (this,
+					position);
+			
+			if (id.Equals (Guid.AsfStreamPropertiesObject))
+				return new StreamPropertiesObject (this,
+					position);
+			
+			if (id.Equals (Guid.AsfContentDescriptionObject))
+				return new ContentDescriptionObject (this,
+					position);
+			
+			if (id.Equals (
+				Guid.AsfExtendedContentDescriptionObject))
+				return new ExtendedContentDescriptionObject (
+					this, position);
+			
+			if (id.Equals (Guid.AsfPaddingObject))
+				return new PaddingObject (this, position);
+			
+			if (id.Equals (Guid.AsfHeaderExtensionObject))
+				return new HeaderExtensionObject (this,
+					position);
+			
+			if (id.Equals (Guid.AsfMetadataLibraryObject))
+				return new MetadataLibraryObject (this,
+					position);
+			
+			return new UnknownObject (this, position);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Reads the contents of the current instance.
+		/// </summary>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		private void Read (ReadStyle propertiesStyle)
+		{
+			Mode = AccessMode.Read;
+			try {
+				HeaderObject header = new HeaderObject (this, 0);
+				
+				if (header.HasContentDescriptors)
+					TagTypesOnDisk |= TagTypes.Asf;
+				
+				asf_tag = new Asf.Tag (header);
+				
+				InvariantStartPosition = (long) header.OriginalSize;
+				InvariantEndPosition = Length;
+				
+				if (propertiesStyle != ReadStyle.None)
+					properties = header.Properties;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/FilePropertiesObject.cs b/lib/TagLib/TagLib/Asf/FilePropertiesObject.cs
new file mode 100644
index 0000000..79021b4
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/FilePropertiesObject.cs
@@ -0,0 +1,319 @@
+//
+// FilePropertiesObject.cs: Provides a representation of an ASF File Properties
+// object which can be read from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF File Properties object which can be read
+	///    from and written to disk.
+	/// </summary>
+	public class FilePropertiesObject : Object
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the GUID for the file.
+		/// </summary>
+		private System.Guid file_id;
+		
+		/// <summary>
+		///    Contains the file size.
+		/// </summary>
+		private ulong file_size;
+		
+		/// <summary>
+		///    Contains the creation date.
+		/// </summary>
+		private ulong creation_date;
+		
+		/// <summary>
+		///    Contains the packet count.
+		/// </summary>
+		private ulong data_packets_count;
+		
+		/// <summary>
+		///    Contains the play duration.
+		/// </summary>
+		private ulong play_duration;
+		
+		/// <summary>
+		///    Contains the send duration.
+		/// </summary>
+		private ulong send_duration;
+		
+		/// <summary>
+		///    Contains the preroll.
+		/// </summary>
+		private ulong preroll;
+		
+		/// <summary>
+		///    Contains the file flags.
+		/// </summary>
+		private uint flags;
+		
+		/// <summary>
+		///    Contains the minimum packet size.
+		/// </summary>
+		private uint minimum_data_packet_size;
+		
+		/// <summary>
+		///    Contains the maxximum packet size.
+		/// </summary>
+		private uint maximum_data_packet_size;
+		
+		/// <summary>
+		///    Contains the maximum bitrate of the file.
+		/// </summary>
+		private uint maximum_bitrate;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="FilePropertiesObject" /> by reading the contents
+		///    from a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or smaller than the minimum size.
+		/// </exception>
+		public FilePropertiesObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			if (!Guid.Equals (Asf.Guid.AsfFilePropertiesObject))
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (OriginalSize < 104)
+				throw new CorruptFileException (
+					"Object size too small.");
+			
+			file_id = file.ReadGuid ();
+			file_size = file.ReadQWord ();
+			creation_date = file.ReadQWord ();
+			data_packets_count = file.ReadQWord ();
+			send_duration = file.ReadQWord ();
+			play_duration = file.ReadQWord ();
+			preroll = file.ReadQWord ();
+			flags = file.ReadDWord ();
+			minimum_data_packet_size = file.ReadDWord ();
+			maximum_data_packet_size = file.ReadDWord ();
+			maximum_bitrate = file.ReadDWord ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the GUID for the file described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Guid" /> value containing the GUID
+		///    for the file described by the current instance.
+		/// </value>
+		public System.Guid FileId {
+			get {return file_id;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the file described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong" /> value containing the size of the
+		///    file described by the current instance.
+		/// </value>
+		public ulong FileSize {
+			get {return file_size;}
+		}
+		
+		/// <summary>
+		///    Gets the creation date of the file described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="DateTime" /> value containing the creation
+		///    date of the file described by the current instance.
+		/// </value>
+		public DateTime CreationDate {
+			get {return new DateTime ((long)creation_date);}
+		}
+		
+		/// <summary>
+		///    Gets the number of data packets in the file described by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong" /> value containing the number of
+		///    data packets in the file described by the current
+		///    instance.
+		/// </value>
+		public ulong DataPacketsCount {
+			get {return data_packets_count;}
+		}
+		
+		/// <summary>
+		///    Gets the play duration of the file described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> value containing the play
+		///    duration of the file described by the current instance.
+		/// </value>
+		public TimeSpan PlayDuration {
+			get {return new TimeSpan ((long)play_duration);}
+		}
+		
+		/// <summary>
+		///    Gets the send duration of the file described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> value containing the send
+		///    duration of the file described by the current instance.
+		/// </value>
+		public TimeSpan SendDuration {
+			get {return new TimeSpan ((long)send_duration);}
+		}
+		
+		/// <summary>
+		///    Gets the pre-roll of the file described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong" /> value containing the pre-roll of
+		///    the file described by the current instance.
+		/// </value>
+		public ulong Preroll {
+			get {return preroll;}
+		}
+		
+		/// <summary>
+		///    Gets the flags of the file described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the flags of the
+		///    file described by the current instance.
+		/// </value>
+		public uint Flags {
+			get {return flags;}
+		}
+		
+		/// <summary>
+		///    Gets the minimum data packet size of the file described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the minimum data
+		///    packet size of the file described by the current
+		///    instance.
+		/// </value>
+		public uint MinimumDataPacketSize {
+			get {return minimum_data_packet_size;}
+		}
+		
+		/// <summary>
+		///    Gets the maximum data packet size of the file described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the maximum data
+		///    packet size of the file described by the current
+		///    instance.
+		/// </value>
+		public uint MaximumDataPacketSize {
+			get {return maximum_data_packet_size;}
+		}
+		
+		/// <summary>
+		///    Gets the maximum bitrate of the file described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the maximum
+		///    bitrate of the file described by the current instance.
+		/// </value>
+		public uint MaximumBitrate {
+			get {return maximum_bitrate;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			ByteVector output = file_id.ToByteArray ();
+			output.Add (RenderQWord (file_size));
+			output.Add (RenderQWord (creation_date));
+			output.Add (RenderQWord (data_packets_count));
+			output.Add (RenderQWord (send_duration));
+			output.Add (RenderQWord (play_duration));
+			output.Add (RenderQWord (preroll));
+			output.Add (RenderDWord (flags));
+			output.Add (RenderDWord (minimum_data_packet_size));
+			output.Add (RenderDWord (maximum_data_packet_size));
+			output.Add (RenderDWord (maximum_bitrate));
+			
+			return Render (output);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/Guid.cs b/lib/TagLib/TagLib/Asf/Guid.cs
new file mode 100644
index 0000000..2f0c353
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/Guid.cs
@@ -0,0 +1,111 @@
+//
+// Guid.cs: Provides common GUID values used by ASF Objects.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This static class contains common <see cref="System.Guid" />
+	///    values used by ASF Objects.
+	/// </summary>
+	public static class Guid
+	{
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="ContentDescriptionObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfContentDescriptionObject =
+			new System.Guid ("75B22633-668E-11CF-A6D9-00AA0062CE6C");
+		
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="ExtendedContentDescriptionObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfExtendedContentDescriptionObject =
+			new System.Guid ("D2D0A440-E307-11D2-97F0-00A0C95EA850");
+		
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="FilePropertiesObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfFilePropertiesObject =
+			new System.Guid ("8CABDCA1-A947-11CF-8EE4-00C00C205365");
+		
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="HeaderExtensionObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfHeaderExtensionObject =
+			new System.Guid ("5FBF03B5-A92E-11CF-8EE3-00C00C205365");
+		
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="HeaderObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfHeaderObject =
+			new System.Guid ("75B22630-668E-11CF-A6D9-00AA0062CE6C");
+		
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="MetadataLibraryObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfMetadataLibraryObject =
+			new System.Guid ("44231C94-9498-49D1-A141-1D134E457054");
+		
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="PaddingObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfPaddingObject =
+			new System.Guid ("1806D474-CADF-4509-A4BA-9AABCB96AAE8");
+		
+		/// <summary>
+		///    Indicates that an object is a <see
+		///    cref="StreamPropertiesObject" />.
+		/// </summary>
+		public static readonly System.Guid AsfStreamPropertiesObject =
+			new System.Guid ("B7DC0791-A9B7-11CF-8EE6-00C00C205365");
+		
+		
+		/// <summary>
+		///    Indicates that a <see cref="StreamPropertiesObject" />
+		///    contains information about an audio stream.
+		/// </summary>
+		public static readonly System.Guid AsfAudioMedia =
+			new System.Guid ("F8699E40-5B4D-11CF-A8FD-00805F5C442B");
+		
+		/// <summary>
+		///    Indicates that a <see cref="StreamPropertiesObject" />
+		///    contains information about an video stream.
+		/// </summary>
+		public static readonly System.Guid AsfVideoMedia =
+			new System.Guid ("BC19EFC0-5B4D-11CF-A8FD-00805F5C442B");
+		
+		/// <summary>
+		///    Indicates a placeholder portion of a file is correctly
+		///    encoded.
+		/// </summary>
+		public static readonly System.Guid AsfReserved1 =
+			new System.Guid ("ABD3D211-A9BA-11cf-8EE6-00C00C205365");
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/HeaderExtensionObject.cs b/lib/TagLib/TagLib/Asf/HeaderExtensionObject.cs
new file mode 100644
index 0000000..fb25943
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/HeaderExtensionObject.cs
@@ -0,0 +1,176 @@
+//
+// HeaderExtensionObject.cs: Provides a representation of an ASF Header
+// Extension object which can be read from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System.Collections.Generic;
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF Header Extension object which can be
+	///    read from and written to disk.
+	/// </summary>
+	public class HeaderExtensionObject : Object
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the child objects.
+		/// </summary>
+		private List<Object> children = new List<Object> ();
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="HeaderExtensionObject" /> by reading the contents
+		///    from a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or contents.
+		/// </exception>
+		public HeaderExtensionObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			if (!Guid.Equals (Asf.Guid.AsfHeaderExtensionObject))
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (file.ReadGuid () != Asf.Guid.AsfReserved1)
+				throw new CorruptFileException (
+					"Reserved1 GUID expected.");
+			
+			if (file.ReadWord () != 6)
+				throw new CorruptFileException (
+					"Invalid reserved WORD. Expected '6'.");
+			
+			uint size_remaining = file.ReadDWord ();
+			position += 0x170 / 8;
+			
+			while (size_remaining > 0) {
+				Object obj = file.ReadObject (position);
+				position += (long) obj.OriginalSize;
+				size_remaining -= (uint) obj.OriginalSize;
+				children.Add (obj);
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the child objects contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the children of the current instance.
+		/// </value>
+		public IEnumerable<Object> Children {
+			get {return children;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			ByteVector output = new ByteVector ();
+			
+			foreach (Object child in children)
+				output.Add (child.Render ());
+			
+			output.Insert (0, RenderDWord ((uint) output.Count));
+			output.Insert (0, RenderWord (6));
+			output.Insert (0, Asf.Guid.AsfReserved1.ToByteArray ());
+			
+			return Render (output);
+		}
+		
+		/// <summary>
+		///    Adds a child object to the current instance.
+		/// </summary>
+		/// <param name="obj">
+		///    A <see cref="Object" /> object to add to the current
+		///    instance.
+		/// </param>
+		public void AddObject (Object obj)
+		{
+			children.Add (obj);
+		}
+		
+		/// <summary>
+		///    Adds a child unique child object to the current instance,
+		///    replacing and existing child if present.
+		/// </summary>
+		/// <param name="obj">
+		///    A <see cref="Object" /> object to add to the current
+		///    instance.
+		/// </param>
+		public void AddUniqueObject (Object obj)
+		{
+			for (int i = 0; i < children.Count; i ++)
+				if (((Object) children [i]).Guid == obj.Guid) {
+					children [i] = obj;
+					return;
+				}
+			
+			children.Add (obj);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/HeaderObject.cs b/lib/TagLib/TagLib/Asf/HeaderObject.cs
new file mode 100644
index 0000000..62b6439
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/HeaderObject.cs
@@ -0,0 +1,275 @@
+//
+// HeaderObject.cs: Provides a representation of an ASF Header object which can
+// be read from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF Header object which can be read from and
+	///    written to disk.
+	/// </summary>
+	public class HeaderObject : Object
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the reserved header data.
+		/// </summary>
+		private ByteVector reserved;
+		
+		/// <summary>
+		///    Contains the child objects.
+		/// </summary>
+		private List<Object> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="HeaderObject" /> by reading the contents from a
+		///    specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or smaller than the minimum size.
+		/// </exception>
+		public HeaderObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			if (!Guid.Equals (Asf.Guid.AsfHeaderObject))
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (OriginalSize < 26)
+				throw new CorruptFileException (
+					"Object size too small.");
+			
+			children = new List<Object> ();
+			
+			uint child_count = file.ReadDWord ();
+			
+			reserved = file.ReadBlock (2);
+			
+			children.AddRange (file.ReadObjects (child_count,
+				file.Tell));
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the header extension object contained in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="HeaderExtensionObject" /> object containing
+		///    the header extension object.
+		/// </value>
+		public HeaderExtensionObject Extension {
+			get {
+				foreach (Object child in children)
+					if (child is HeaderExtensionObject)
+						return child as HeaderExtensionObject;
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the child objects contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the children of the current instance.
+		/// </value>
+		public IEnumerable<Object> Children {
+			get {return children;}
+		}
+		
+		/// <summary>
+		///    Gets the media properties contained within the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="Properties" /> object containing the media
+		///    properties of the current instance.
+		/// </value>
+		public Properties Properties {
+			get {
+				TimeSpan duration = TimeSpan.Zero;
+				List<ICodec> codecs = new List<ICodec> ();
+				
+				foreach (Object obj in Children) {
+					FilePropertiesObject fpobj = obj as
+						FilePropertiesObject;
+					
+					if (fpobj != null) {
+						duration = fpobj.PlayDuration -
+							new TimeSpan((long) fpobj.Preroll);
+						continue;
+					}
+					
+					StreamPropertiesObject spobj = obj as
+						StreamPropertiesObject;
+					
+					if (spobj != null) {
+						codecs.Add (spobj.Codec);
+						continue;
+					}
+				}
+				
+				return new Properties (duration, codecs);
+			}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance contains either
+		///    type of content descriptiors.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance contains
+		///    a <see cref="ContentDescriptionObject" /> or a <see
+		///    cref="ExtendedContentDescriptionObject" />. Otherwise
+		///    <see langword="false" />.
+		/// </value>
+		public bool HasContentDescriptors {
+			get {
+				foreach (Asf.Object child in children)
+					if (child.Guid == Asf.Guid.AsfContentDescriptionObject ||
+						child.Guid == Asf.Guid.AsfExtendedContentDescriptionObject)
+						return true;
+				
+				return false;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			ByteVector output = new ByteVector ();
+			uint child_count = 0;
+			
+			foreach (Object child in children)
+				if (child.Guid != Asf.Guid.AsfPaddingObject) {
+					output.Add (child.Render ());
+					child_count ++;
+				}
+			
+			long size_diff = (long) output.Count + 30 -
+				(long) OriginalSize;
+			
+			if (size_diff != 0) {
+				PaddingObject obj = new PaddingObject ((uint)
+					(size_diff > 0 ? 4096 : - size_diff));
+				
+				output.Add (obj.Render ());
+				child_count ++;
+			}
+			
+			output.Insert (0, reserved);
+			output.Insert (0, RenderDWord (child_count));
+			return Render (output);
+		}
+		
+		/// <summary>
+		///    Adds a child object to the current instance.
+		/// </summary>
+		/// <param name="obj">
+		///    A <see cref="Object" /> object to add to the current
+		///    instance.
+		/// </param>
+		public void AddObject (Object obj)
+		{
+			children.Add (obj);
+		}
+		
+		/// <summary>
+		///    Adds a child unique child object to the current instance,
+		///    replacing and existing child if present.
+		/// </summary>
+		/// <param name="obj">
+		///    A <see cref="Object" /> object to add to the current
+		///    instance.
+		/// </param>
+		public void AddUniqueObject (Object obj)
+		{
+			for (int i = 0; i < children.Count; i ++)
+				if (children [i].Guid == obj.Guid) {
+					children [i] = obj;
+					return;
+				}
+			
+			children.Add (obj);
+		}
+		
+		/// <summary>
+		///    Removes the content description objects from the current
+		///    instance.
+		/// </summary>
+		public void RemoveContentDescriptors ()
+		{
+			for (int i = children.Count - 1; i >= 0; i --)
+				if (children [i].Guid == Asf.Guid.AsfContentDescriptionObject ||
+					children [i].Guid == Asf.Guid.AsfExtendedContentDescriptionObject)
+					children.RemoveAt (i);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/MetadataLibraryObject.cs b/lib/TagLib/TagLib/Asf/MetadataLibraryObject.cs
new file mode 100644
index 0000000..f785cfb
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/MetadataLibraryObject.cs
@@ -0,0 +1,298 @@
+//
+// MetadataLibraryObject.cs: Provides a representation of an ASF Metadata
+// Library object which can be read from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF Metadata Library object which can be
+	///    read from and written to disk.
+	/// </summary>
+	public class MetadataLibraryObject : Object,
+		IEnumerable<DescriptionRecord>
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the description records.
+		/// </summary>
+		private List<DescriptionRecord> records =
+			new List<DescriptionRecord> ();
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MetadataLibraryObject" /> by reading the contents
+		///    from a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or smaller than the minimum size.
+		/// </exception>
+		public MetadataLibraryObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			if (!Guid.Equals (Asf.Guid.AsfMetadataLibraryObject))
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (OriginalSize < 26)
+				throw new CorruptFileException (
+					"Object size too small.");
+			
+			ushort count = file.ReadWord ();
+			
+			for (ushort i = 0; i < count; i ++) {
+				DescriptionRecord rec = new DescriptionRecord (
+					file);
+				AddRecord (rec);
+			}
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MetadataLibraryObject" /> with no contents.
+		/// </summary>
+		public MetadataLibraryObject ()
+			: base (Asf.Guid.AsfMetadataLibraryObject)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance doesn't
+		///    contain any <see cref="DescriptionRecord" /> objects.
+		///    Otherwise <see langword="false" />.
+		/// </value>
+		public bool IsEmpty {
+			get {return records.Count == 0;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			ByteVector output = new ByteVector ();
+			ushort count = 0;
+			
+			foreach (DescriptionRecord rec in records) {
+				count ++;
+				output.Add (rec.Render ());
+			}
+			
+			return Render (RenderWord (count) + output);
+		}
+		
+		/// <summary>
+		///    Removes all records with a given language, stream, and
+		///    name from the current instance.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the records to be removed.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the records to be removed.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    records to be removed.
+		/// </param>
+		public void RemoveRecords (ushort languageListIndex,
+		                           ushort streamNumber,
+		                           string name)
+		{
+			for (int i = records.Count - 1; i >= 0; i --) {
+				DescriptionRecord rec = records [i];
+				if (rec.LanguageListIndex == languageListIndex &&
+					rec.StreamNumber == streamNumber &&
+					rec.Name == name)
+					records.RemoveAt (i);
+			}
+		}
+		
+		/// <summary>
+		///    Gets all records with a given language, stream, and any
+		///    of a collection of names from the current instance.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the records to be retrieved.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the records to be retrieved.
+		/// </param>
+		/// <param name="names">
+		///    A <see cref="string[]" /> containing the names of the
+		///    records to be retrieved.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the <see cref="DescriptionRecord" /> objects
+		///    retrieved from the current instance.
+		/// </returns>
+		public IEnumerable<DescriptionRecord> GetRecords (ushort languageListIndex,
+		                                                  ushort streamNumber,
+		                                                  params string [] names)
+		{
+			foreach (DescriptionRecord rec in records) {
+				if (rec.LanguageListIndex != languageListIndex ||
+					rec.StreamNumber != streamNumber)
+					continue;
+				
+				foreach (string name in names)
+					if (rec.Name == name)
+						yield return rec;
+			}
+		}
+		
+		/// <summary>
+		///    Adds a record to the current instance.
+		/// </summary>
+		/// <param name="record">
+		///    A <see cref="DescriptionRecord" /> object to add to the
+		///    current instance.
+		/// </param>
+		public void AddRecord (DescriptionRecord record)
+		{
+			records.Add (record);
+		}
+		
+		/// <summary>
+		///    Sets the a collection of records for a given language,
+		///    stream, and name, removing the existing matching records.
+		/// </summary>
+		/// <param name="languageListIndex">
+		///    A <see cref="ushort" /> value containing the language
+		///    list index of the records to be added.
+		/// </param>
+		/// <param name="streamNumber">
+		///    A <see cref="ushort" /> value containing the stream
+		///    number of the records to be added.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    records to be added.
+		/// </param>
+		/// <param name="records">
+		///    A <see cref="DescriptionRecord[]" /> containing records
+		///    to add to the new instance.
+		/// </param>
+		/// <remarks>
+		///    All added entries in <paramref name="records" /> should
+		///    match <paramref name="languageListIndex" />, <paramref
+		///    name="streamNumber" /> and <paramref name="name" /> but
+		///    it is not verified by the method. The records will be
+		///    added with their own values and not those provided in
+		///    this method, which are used for removing existing values
+		///    and determining where to position the new object.
+		/// </remarks>
+		public void SetRecords (ushort languageListIndex,
+		                        ushort streamNumber, string name,
+		                        params DescriptionRecord [] records)
+		{
+			int position = this.records.Count;
+			for (int i = this.records.Count - 1; i >= 0; i --) {
+				DescriptionRecord rec = this.records [i];
+				if (rec.LanguageListIndex == languageListIndex &&
+					rec.StreamNumber == streamNumber &&
+					rec.Name == name) {
+					this.records.RemoveAt (i);
+					position = i;
+				}
+			}
+			this.records.InsertRange (position, records);
+		}
+		
+		#endregion
+		
+		
+		
+#region IEnumerable
+		
+		/// <summary>
+		///    Gets an enumerator for enumerating through the
+		///    description records.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the description records.
+		/// </returns>
+		public IEnumerator<DescriptionRecord> GetEnumerator ()
+		{
+			return records.GetEnumerator ();
+		}
+		
+		System.Collections.IEnumerator
+			System.Collections.IEnumerable.GetEnumerator ()
+		{
+			return records.GetEnumerator ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/Object.cs b/lib/TagLib/TagLib/Asf/Object.cs
new file mode 100644
index 0000000..91cbdaa
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/Object.cs
@@ -0,0 +1,253 @@
+//
+// Object.cs: Provides a basic representation of an ASF object which can be read
+// from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This abstract class provides a basic representation of an ASF
+	///    object which can be read from and written to disk.
+	/// </summary>
+	public abstract class Object
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the GUID of the object.
+		/// </summary>
+		private System.Guid id;
+		
+		/// <summary>
+		///    Contains the size of the object on disk.
+		/// </summary>
+		private ulong size;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Object" /> by reading the contents from a
+		///    specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		protected Object (Asf.File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (position < 0 ||
+				position > file.Length - 24)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			file.Seek (position);
+			id = file.ReadGuid ();
+			size = file.ReadQWord ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Object" /> with a specified GUID.
+		/// </summary>
+		/// <param name="guid">
+		///    A <see cref="System.Guid" /> value containing the GUID to
+		///    use for the new instance.
+		/// </param>
+		protected Object (System.Guid guid)
+		{
+			id = guid;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the GUID for the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Guid" /> object containing the GUID
+		///    of the current instance.
+		/// </value>
+		public System.Guid Guid {
+			get {return id;}
+		}
+		
+		/// <summary>
+		///    Gets the original size of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong" /> value containing the size of the
+		///    current instance as it originally appeared on disk.
+		/// </value>
+		public ulong OriginalSize {
+			get {return size;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		/// <seealso cref="Render(ByteVector)" />
+		public abstract ByteVector Render ();
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Renders a Unicode (wide) string.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string" /> object containing the text to
+		///    render.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered value.
+		/// </returns>
+		public static ByteVector RenderUnicode (string value)
+		{
+			ByteVector v = ByteVector.FromString (value,
+				StringType.UTF16LE);
+			v.Add (RenderWord (0));
+			return v;
+		}
+		
+		/// <summary>
+		///    Renders a 4-byte DWORD.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="uint" /> value containing the DWORD to
+		///    render.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered value.
+		/// </returns>
+		public static ByteVector RenderDWord (uint value)
+		{
+			return ByteVector.FromUInt (value, false);
+		}
+		
+		/// <summary>
+		///    Renders a 8-byte QWORD.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="ulong" /> value containing the QWORD to
+		///    render.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered value.
+		/// </returns>
+		public static ByteVector RenderQWord (ulong value)
+		{
+			return ByteVector.FromULong (value, false);
+		}
+		
+		/// <summary>
+		///    Renders a 2-byte WORD.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="ushort" /> value containing the WORD to
+		///    render.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered value.
+		/// </returns>
+		public static ByteVector RenderWord (ushort value)
+		{
+			return ByteVector.FromUShort (value, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object
+		///    containing specified data.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the data to
+		///    contained in the rendered version of the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		/// <remarks>
+		///    Child classes implementing <see cref="Render()" /> should
+		///    render their contents and then send the data through this
+		///    method to produce the final output.
+		/// </remarks>
+		protected ByteVector Render (ByteVector data)
+		{
+			ulong length = (ulong)
+				((data != null ? data.Count : 0) + 24);
+			ByteVector v = id.ToByteArray ();
+			v.Add (RenderQWord (length));
+			v.Add (data);
+			return v;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/PaddingObject.cs b/lib/TagLib/TagLib/Asf/PaddingObject.cs
new file mode 100644
index 0000000..dfd905c
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/PaddingObject.cs
@@ -0,0 +1,139 @@
+//
+// PaddingObject.cs: Provides a representation of an ASF Padding object which
+// can be read from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF Padding object which can be read from
+	///    and written to disk.
+	/// </summary>
+	public class PaddingObject : Object
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the size of the current instance.
+		/// </summary>
+		private ulong size;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PaddingObject" /> by reading the contents from a
+		///    specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or smaller than the minimum size.
+		/// </exception>
+		public PaddingObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			if (!Guid.Equals (Asf.Guid.AsfPaddingObject))
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (OriginalSize < 24)
+				throw new CorruptFileException (
+					"Object size too small.");
+			
+			size = OriginalSize;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PaddingObject" /> of a specified size.
+		/// </summary>
+		/// <param name="size">
+		///    A <see cref="uint" /> value specifying the number of
+		///    bytes the new instance is to take up on disk.
+		/// </param>
+		public PaddingObject (uint size)
+			: base (Asf.Guid.AsfPaddingObject)
+		{
+			this.size = size;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Prublic Properties
+		
+		/// <summary>
+		///    Gets and sets the number of bytes the current instance
+		///    will take up on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong" /> value containing the size of the
+		///    current instance on disk.
+		/// </value>
+		public ulong Size {
+			get {return size;}
+			set {size = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			return Render (new ByteVector ((int) (size - 24)));
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/StreamPropertiesObject.cs b/lib/TagLib/TagLib/Asf/StreamPropertiesObject.cs
new file mode 100644
index 0000000..1602ead
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/StreamPropertiesObject.cs
@@ -0,0 +1,270 @@
+//
+// StreamPropertiesObject.cs: Provides a representation of an ASF Stream
+// Properties object which can be read from and written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Text;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an ASF Stream Properties object which can be
+	///    read from and written to disk.
+	/// </summary>
+	public class StreamPropertiesObject : Object
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the stream type GUID.
+		/// </summary>
+		private System.Guid stream_type;
+		
+		/// <summary>
+		///    Contains the error correction type GUID.
+		/// </summary>
+		private System.Guid error_correction_type;
+		
+		/// <summary>
+		///    Contains the time offset of the stream.
+		/// </summary>
+		private ulong time_offset;
+		
+		/// <summary>
+		///    Contains the stream flags.
+		/// </summary>
+		private ushort flags;
+		
+		/// <summary>
+		///    Contains the reserved data.
+		/// </summary>
+		private uint reserved;
+		
+		/// <summary>
+		///    Contains the type specific data.
+		/// </summary>
+		private ByteVector type_specific_data;
+		
+		/// <summary>
+		///    Contains the error correction data.
+		/// </summary>
+		private ByteVector error_correction_data;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PaddingObject" /> by reading the contents from a
+		///    specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The object read from disk does not have the correct GUID
+		///    or smaller than the minimum size.
+		/// </exception>
+		public StreamPropertiesObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			if (!Guid.Equals (Asf.Guid.AsfStreamPropertiesObject))
+				throw new CorruptFileException (
+					"Object GUID incorrect.");
+			
+			if (OriginalSize < 78)
+				throw new CorruptFileException (
+					"Object size too small.");
+			
+			stream_type = file.ReadGuid ();
+			error_correction_type = file.ReadGuid ();
+			time_offset = file.ReadQWord ();
+			
+			int type_specific_data_length = (int) file.ReadDWord ();
+			int error_correction_data_length = (int)
+				file.ReadDWord ();
+			
+			flags = file.ReadWord ();
+			reserved = file.ReadDWord ();
+			type_specific_data =
+				file.ReadBlock (type_specific_data_length);
+			error_correction_data =
+				file.ReadBlock (error_correction_data_length);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the codec information contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ICodec" /> object containing the codec
+		///    information read from <see cref="TypeSpecificData" /> or
+		///    <see langword="null" /> if the data could not be decoded.
+		/// </value>
+		public ICodec Codec {
+			get {
+				if (stream_type == Asf.Guid.AsfAudioMedia)
+					return new Riff.WaveFormatEx (
+						type_specific_data, 0);
+				
+				if (stream_type == Asf.Guid.AsfVideoMedia)
+					return new TagLib.Riff.BitmapInfoHeader (
+						type_specific_data, 11);
+				
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the stream type GUID of the current instance.
+		/// </summary>
+		/// <summary>
+		///    A <see cref="System.Guid" /> object containing the stream
+		///    type GUID of the current instance.
+		/// </summary>
+		public System.Guid StreamType {
+			get {return stream_type;}
+		}
+		
+		/// <summary>
+		///    Gets the error correction type GUID of the current
+		///    instance.
+		/// </summary>
+		/// <summary>
+		///    A <see cref="System.Guid" /> object containing the error
+		///    correction type GUID of the current instance.
+		/// </summary>
+		public System.Guid ErrorCorrectionType {
+			get {return error_correction_type;}
+		}
+		
+		/// <summary>
+		///    Gets the time offset at which the stream described by the
+		///    current instance begins.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> value containing the time
+		///    offset at which the stream described by the current
+		///    instance begins.
+		/// </value>
+		public TimeSpan TimeOffset {
+			get {return new TimeSpan ((long)time_offset);}
+		}
+		
+		/// <summary>
+		///    Gets the flags that apply to the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value containing the flags that
+		///    apply to the current instance.
+		/// </value>
+		public ushort Flags {
+			get {return flags;}
+		}
+		
+		/// <summary>
+		///    Gets the type specific data contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the type
+		///    specific data contained in the current instance.
+		/// </value>
+		/// <remarks>
+		///    The contents of this value are dependant on the type
+		///    contained in <see cref="StreamType" />.
+		/// </remarks>
+		public ByteVector TypeSpecificData {
+			get {return type_specific_data;}
+		}
+		
+		/// <summary>
+		///    Gets the error correction data contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the error
+		///    correction data contained in the current instance.
+		/// </value>
+		/// <remarks>
+		///    The contents of this value are dependant on the type
+		///    contained in <see cref="ErrorCorrectionType" />.
+		/// </remarks>
+		public ByteVector ErrorCorrectionData {
+			get {return error_correction_data;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			ByteVector output = stream_type.ToByteArray ();
+			output.Add (error_correction_type.ToByteArray ());
+			output.Add (RenderQWord (time_offset));
+			output.Add (RenderDWord ((uint)
+				type_specific_data.Count));
+			output.Add (RenderDWord ((uint)
+				error_correction_data.Count));
+			output.Add (RenderWord  (flags));
+			output.Add (RenderDWord (reserved));
+			output.Add (type_specific_data);
+			output.Add (error_correction_data);
+			
+			return Render (output);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/Tag.cs b/lib/TagLib/TagLib/Asf/Tag.cs
new file mode 100644
index 0000000..9b44a2b
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/Tag.cs
@@ -0,0 +1,1416 @@
+//
+// Tag.cs: Provides a representation of an ASF tag which can be read from and
+// written to disk.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace TagLib.Asf {
+	/// <summary>
+	///    This class extends <see cref="TagLib.Tag" /> to provide a
+	///    representation of an ASF tag which can be read from and written
+	///    to disk.
+	/// </summary>
+	public class Tag : TagLib.Tag, IEnumerable<ContentDescriptor>
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the content description object.
+		/// </summary>
+		private ContentDescriptionObject description =
+			new ContentDescriptionObject ();
+		
+		/// <summary>
+		///    Contains the extended content description object.
+		/// </summary>
+		private ExtendedContentDescriptionObject ext_description =
+			new ExtendedContentDescriptionObject ();
+		
+		/// <summary>
+		///    Contains the metadata library object.
+		/// </summary>
+		private MetadataLibraryObject metadata_library =
+			new MetadataLibraryObject ();
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> with no contents.
+		/// </summary>
+		public Tag ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> using the children of a <see
+		///    cref="HeaderObject" /> object.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="HeaderObject" /> object whose children are
+		///    are to be used by the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="header" /> is <see langword="null" />.
+		/// </exception>
+		public Tag (HeaderObject header)
+		{
+			if (header == null)
+				throw new ArgumentNullException ("header");
+			
+			foreach (Object child in header.Children) {
+				if (child is ContentDescriptionObject)
+					description =
+						child as ContentDescriptionObject;
+			
+				if (child is ExtendedContentDescriptionObject)
+					ext_description =
+						child as ExtendedContentDescriptionObject;
+			}
+			
+			foreach (Object child in header.Extension.Children)
+				if (child is MetadataLibraryObject)
+					metadata_library =
+						child as MetadataLibraryObject;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the ASF Content Description object used by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ContentDescriptionObject" /> object
+		///    containing the ASF Content Description object used by the
+		///    current instance.
+		/// </value>
+		public ContentDescriptionObject ContentDescriptionObject {
+			get {return description;}
+		}
+		
+		/// <summary>
+		///    Gets the ASF Extended Content Description object used by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ExtendedContentDescriptionObject" /> object
+		///    containing the ASF Extended Content Description object
+		///    used by the current instance.
+		/// </value>
+		public ExtendedContentDescriptionObject
+			ExtendedContentDescriptionObject {
+			get {return ext_description;}
+		}
+		
+		/// <summary>
+		///    Gets the ASF Metadata Library object used by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="MetadataLibraryObject" /> object containing
+		///    the ASF Metadata Library object used by the current
+		///    instance.
+		/// </value>
+		public MetadataLibraryObject MetadataLibraryObject {
+			get {return metadata_library;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets the string contained in a specific descriptor in the
+		///    current instance.
+		/// </summary>
+		/// <param name="names">
+		///    A <see cref="string[]" /> containing the names of the
+		///    descriptors to look for the value in.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="names" /> is <see langword="null" />.
+		/// </exception>
+		/// <returns>
+		///    A <see cref="string" /> object containing the contents of
+		///    the first descriptor found in the current instance.
+		/// </returns>
+		public string GetDescriptorString (params string [] names)
+		{
+			if (names == null)
+				throw new ArgumentNullException ("names");
+			
+			foreach (ContentDescriptor desc in GetDescriptors (names)) {
+				if (desc == null || desc.Type != DataType.Unicode)
+					continue;
+				
+				string value = desc.ToString ();
+				if (value != null)
+					return value;
+			}
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Gets the strings contained in a specific descriptor in
+		///    the current instance.
+		/// </summary>
+		/// <param name="names">
+		///    A <see cref="string[]" /> containing the names of the
+		///    descriptors to look for the value in.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="names" /> is <see langword="null" />.
+		/// </exception>
+		/// <returns>
+		///    A <see cref="string" /> object containing the contents of
+		///    the first descriptor found in the current instance as
+		///    split by ';'.
+		/// </returns>
+		public string [] GetDescriptorStrings (params string [] names)
+		{
+			if (names == null)
+				throw new ArgumentNullException ("names");
+			
+			return SplitAndClean (GetDescriptorString (names));
+		}
+		
+		/// <summary>
+		///    Sets the string for a collection of descriptors in the
+		///    current instance.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string" /> object containing the value to
+		///    store, or <see langword="null" /> to clear the value.
+		/// </param>
+		/// <param name="names">
+		///    A <see cref="string[]" /> containing the names in which
+		///    the value would be expected. For example, "WM/AlbumTitle"
+		///    and "Album".
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="names" /> is <see langword="null" />.
+		/// </exception>
+		/// <remarks>
+		///    The value will be stored in the first value in <paramref
+		///    name="names" /> and the rest will be cleared.
+		/// </remarks>
+		public void SetDescriptorString (string value,
+		                                 params string [] names)
+		{
+			if (names == null)
+				throw new ArgumentNullException ("names");
+			
+			int index = 0;
+			
+			if (value != null && value.Trim ().Length != 0) {
+				SetDescriptors (names [0],
+					new ContentDescriptor (names [0], value));
+				
+				index ++;
+			}
+			
+			for (; index < names.Length; index ++)
+				RemoveDescriptors (names [index]);
+		}
+		
+		/// <summary>
+		///    Sets the strings for a collection of descriptors in the
+		///    current instance.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string[]" /> containing the value to store,
+		///    or <see langword="null" /> to clear the value.
+		/// </param>
+		/// <param name="names">
+		///    A <see cref="string[]" /> containing the names in which
+		///    the value would be expected. For example, "WM/AlbumTitle"
+		///    and "Album".
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="names" /> is <see langword="null" />.
+		/// </exception>
+		/// <remarks>
+		///    The value will be stored in the first value in <paramref
+		///    name="names" /> and the rest will be cleared.
+		/// </remarks>
+		public void SetDescriptorStrings (string [] value,
+		                                  params string [] names)
+		{
+			if (names == null)
+				throw new ArgumentNullException ("names");
+			
+			SetDescriptorString (String.Join ("; ", value), names);
+		}
+		
+		/// <summary>
+		///    Removes all descriptors with a specified name from the
+		///    current instance.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    descriptor to remove from the current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="name" /> is <see langword="null" />.
+		/// </exception>
+		public void RemoveDescriptors (string name)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+			
+			ext_description.RemoveDescriptors (name);
+		}
+		
+		/// <summary>
+		///    Gets all descriptors with any of a collection of names
+		///    from the current instance.
+		/// </summary>
+		/// <param name="names">
+		///    A <see cref="string[]" /> containing the names of the
+		///    descriptors to be retrieved.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="names" /> is <see langword="null" />.
+		/// </exception>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the <see cref="ContentDescriptor" /> objects
+		///    retrieved from the current instance.
+		/// </returns>
+		public IEnumerable<ContentDescriptor> GetDescriptors (params string [] names)
+		{
+			if (names == null)
+				throw new ArgumentNullException ("names");
+			
+			return ext_description.GetDescriptors (names);
+		}
+		
+		/// <summary>
+		///    Sets the a collection of desciptors for a given name,
+		///    removing the existing matching records.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    descriptors to be added.
+		/// </param>
+		/// <param name="descriptors">
+		///    A <see cref="ContentDescriptor[]" /> containing
+		///    descriptors to add to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="name" /> is <see langword="null" />.
+		/// </exception>
+		/// <remarks>
+		///    All added entries in <paramref name="descriptors" />
+		///    should match <paramref name="name" /> but it is not
+		///    verified by the method. The descriptors will be added
+		///    with their own names and not the one provided in this
+		///    method, which are used for removing existing values and
+		///    determining where to position the new objects.
+		/// </remarks>
+		public void SetDescriptors (string name,
+		                            params ContentDescriptor [] descriptors)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+			
+			ext_description.SetDescriptors (name, descriptors);
+		}
+		
+		/// <summary>
+		///    Adds a descriptor to the current instance.
+		/// </summary>
+		/// <param name="descriptor">
+		///    A <see cref="ContentDescriptor" /> object to add to the
+		///    current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="descriptor" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public void AddDescriptor (ContentDescriptor descriptor)
+		{
+			if (descriptor == null)
+				throw new ArgumentNullException ("descriptor");
+			
+			ext_description.AddDescriptor (descriptor);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Static Methods
+		
+		/// <summary>
+		///    Converts a raw ASF picture into an <see cref="IPicture"
+		///    /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing raw ASF
+		///    picture data.
+		/// </param>
+		/// <returns>
+		///    A <see cref="IPicture" /> object to read from the raw
+		///    data.
+		/// </returns>
+		private static IPicture PictureFromData (ByteVector data)
+		{
+			if (data.Count < 9)
+				return null;
+			
+			int offset = 0;
+			Picture p = new Picture ();
+			
+			// Get the picture type:
+			
+			p.Type = (PictureType) data [offset];
+			offset += 1;
+			
+			// Get the picture size:
+			
+			int size = (int) data.Mid (offset, 4).ToUInt (false);
+			offset += 4;
+			
+			// Get the mime-type:
+			
+			int found = data.Find (ByteVector.TextDelimiter (
+				StringType.UTF16LE), offset, 2);
+			if (found < 0)
+				return null;
+			
+			p.MimeType = data.ToString (StringType.UTF16LE, offset,
+				found - offset);
+			offset = found + 2;
+			
+			// Get the description:
+			
+			found = data.Find (ByteVector.TextDelimiter (
+				StringType.UTF16LE), offset, 2);
+			if (found < 0)
+				return null;
+			
+			p.Description = data.ToString (StringType.UTF16LE,
+				offset, found - offset);
+			offset = found + 2;
+			
+			p.Data = data.Mid (offset, size);
+			
+			return p;
+		}
+		
+		/// <summary>
+		///    Converts a <see cref="IPicture" /> object into raw ASF
+		///    picture data.
+		/// </summary>
+		/// <param name="picture">
+		///    A <see cref="IPicture" /> object to convert.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing raw ASF
+		///    picture data.
+		/// </returns>
+		private static ByteVector PictureToData (IPicture picture)
+		{
+			ByteVector v = new ByteVector ((byte) picture.Type);
+			v.Add (Object.RenderDWord ((uint) picture.Data.Count));
+			v.Add (Object.RenderUnicode (picture.MimeType));
+			v.Add (Object.RenderUnicode (picture.Description));
+			v.Add (picture.Data);
+			return v;
+		}
+		
+		/// <summary>
+		///    Splits a string into a collection of strings by ';'.
+		/// </summary>
+		/// <param name="s">
+		///    A <see cref="string" /> object containing the text to
+		///    split.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the split text.
+		/// </returns>
+		private static string [] SplitAndClean (string s)
+		{
+			if (s == null || s.Trim ().Length == 0)
+				return new string [0];
+			
+			string [] result = s.Split (';');
+			
+			for (int i = 0; i < result.Length; i ++)
+				result [i] = result [i].Trim ();
+			
+			return result;
+		}
+		
+		#endregion
+		
+		
+		
+#region IEnumerable
+		
+		/// <summary>
+		///    Gets an enumerator for enumerating through the content
+		///    descriptors.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the content descriptors.
+		/// </returns>
+		public IEnumerator<ContentDescriptor> GetEnumerator ()
+		{
+			return ext_description.GetEnumerator ();
+		}
+		
+		System.Collections.IEnumerator
+			System.Collections.IEnumerable.GetEnumerator ()
+		{
+			return ext_description.GetEnumerator ();
+		}
+		
+#endregion
+		
+		
+		
+		#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.Asf" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.Asf;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the title stored in
+		///    the ASF Content Description Object.
+		/// </remarks>
+		public override string Title {
+			get {return description.Title;}
+			set {description.Title = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the Track Title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort name of 
+		///    the Track Title of the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/TitleSortOrder"
+		///    field.
+		///    http://msdn.microsoft.com/en-us/library/aa386866(VS.85).aspx
+		/// </remarks>
+		public override string TitleSort {
+			get {
+				return GetDescriptorString ("WM/TitleSortOrder");
+			}
+			set {
+				SetDescriptorString (value, "WM/TitleSortOrder");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the author stored in
+		///    the ASF Content Description Object.
+		/// </remarks>
+		public override string [] Performers {
+			get {return SplitAndClean (description.Author);}
+			set {description.Author = string.Join ("; ", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/ArtistSortOrder" field.
+		///    http://msdn.microsoft.com/en-us/library/aa386866(VS.85).aspx
+		/// </remarks>
+		public override string [] PerformersSort {
+			get {
+				return GetDescriptorStrings ("WM/ArtistSortOrder");
+			}
+			set {
+				SetDescriptorStrings (value, "WM/ArtistSortOrder");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/AlbumArtist"
+		///    and "AlbumArtist" Content Descriptors.
+		/// </remarks>
+		public override string [] AlbumArtists {
+			get {
+				return GetDescriptorStrings ("WM/AlbumArtist",
+					"AlbumArtist");
+			}
+			set {
+				SetDescriptorStrings (value, "WM/AlbumArtist",
+					"AlbumArtist");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the band or artist who
+		///    is credited in the creation of the entire album or
+		///    collection containing the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the band or artist who is credited in the creation
+		///    of the entire album or collection containing the media
+		///    described by the current instance or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/AlbumArtistSortOrder"
+		///    field.
+		///    http://msdn.microsoft.com/en-us/library/aa386866(VS.85).aspx
+		/// </remarks>
+		public override string [] AlbumArtistsSort {
+			get {
+				return GetDescriptorStrings ("WM/AlbumArtistSortOrder");
+			}
+			set {
+				SetDescriptorStrings (value, "WM/AlbumArtistSortOrder");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/Composer"
+		///    and "Composer" Content Descriptors.
+		/// </remarks>
+		public override string [] Composers {
+			get {
+				return GetDescriptorStrings ("WM/Composer",
+					"Composer");
+			}
+			set {
+				SetDescriptorStrings (value, "WM/Composer",
+					"Composer");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/AlbumTitle"
+		///    and "Album" Content Descriptors.
+		/// </remarks>
+		public override string Album {
+			get {
+				return GetDescriptorString ("WM/AlbumTitle",
+					"Album");
+			}
+			set {
+				SetDescriptorString (value, "WM/AlbumTitle",
+					"Album");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the Album Title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort name of 
+		///    the Album Title of the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/AlbumSortOrder"
+		///    field.
+		///    http://msdn.microsoft.com/en-us/library/aa386866(VS.85).aspx
+		/// </remarks>
+		public override string AlbumSort {
+			get {
+				return GetDescriptorString ("WM/AlbumSortOrder");
+			}
+			set {
+				SetDescriptorString (value, "WM/AlbumSortOrder");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the description stored
+		///    in the ASF Content Description Object.
+		/// </remarks>
+		public override string Comment {
+			get {return description.Description;}
+			set {description.Description = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/Genre",
+		///    "WM/GenreID", and "Genre" Content Descriptors.
+		/// </remarks>
+		public override string [] Genres {
+			get {
+				string value = GetDescriptorString ("WM/Genre",
+					"WM/GenreID", "Genre");
+				
+				if (value == null || value.Trim ().Length == 0)
+					return new string [] {};
+				
+				string [] result = value.Split (';');
+				
+				for (int i = 0; i < result.Length; i ++) {
+					string genre = result [i].Trim ();
+					
+					byte genre_id;
+					int closing = genre.IndexOf (')');
+					if (closing > 0 && genre[0] == '(' &&
+						byte.TryParse (genre.Substring (
+						1, closing - 1), out genre_id))
+						genre = TagLib.Genres
+							.IndexToAudio (genre_id);
+					
+					result [i] = genre;
+				}
+				
+				return result;
+			}
+			set {
+				SetDescriptorString (String.Join ("; ", value),
+					"WM/Genre", "Genre", "WM/GenreID");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/Year" Content
+		///    Descriptor.
+		/// </remarks>
+		public override uint Year {
+			get {
+				string text = GetDescriptorString ("WM/Year");
+				
+				if (text == null || text.Length < 4)
+					return 0;
+				
+				uint value;
+				if (uint.TryParse (text.Substring (0, 4),
+					NumberStyles.Integer,
+					CultureInfo.InvariantCulture,
+					out value))
+					return value;
+				
+				return 0;
+			}
+			set {
+				if (value == 0) {
+					RemoveDescriptors ("WM/Year");
+					return;
+				}
+				
+				SetDescriptorString (
+					value.ToString (
+						CultureInfo.InvariantCulture),
+					"WM/Year");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/TrackNumber"
+		///    Content Descriptor.
+		/// </remarks>
+		public override uint Track {
+			get {
+				foreach (ContentDescriptor desc in
+					GetDescriptors ("WM/TrackNumber")) {
+						uint value = desc.ToDWord ();
+						if (value != 0)
+							return value;
+				}
+				
+				return 0;
+			}
+			set {
+				if (value == 0)
+					RemoveDescriptors ("WM/TrackNumber");
+				else
+					SetDescriptors ("WM/TrackNumber",
+						new ContentDescriptor (
+							"WM/TrackNumber",
+							value));
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TrackTotal"
+		///    Content Descriptor.
+		/// </remarks>
+		public override uint TrackCount {
+			get {
+				foreach (ContentDescriptor desc in
+					GetDescriptors ("TrackTotal")) {
+						uint value = desc.ToDWord ();
+						if (value != 0)
+							return value;
+				}
+				
+				return 0;
+			}
+			set {
+				if (value == 0)
+					RemoveDescriptors ("TrackTotal");
+				else
+					SetDescriptors ("TrackTotal",
+						new ContentDescriptor (
+							"TrackTotal", value));
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/PartOfSet"
+		///    Content Descriptor.
+		/// </remarks>
+		public override uint Disc {
+			get {
+				string text = GetDescriptorString (
+					"WM/PartOfSet");
+				
+				if (text == null)
+					return 0;
+				
+				string [] texts = text.Split ('/');
+				uint value;
+				
+				if (texts.Length < 1)
+					return 0;
+				
+				return uint.TryParse (texts [0],
+					NumberStyles.Integer,
+					CultureInfo.InvariantCulture,
+					out value) ? value : 0;
+			}
+			set {
+				uint count = DiscCount;
+				if (value == 0 && count == 0) {
+					RemoveDescriptors ("WM/PartOfSet");
+					return;
+				}
+				
+				if (count != 0) {
+					SetDescriptorString (string.Format (
+						CultureInfo.InvariantCulture,
+						"{0}/{1}", value, count),
+						"WM/PartOfSet");
+					return;
+				}
+				
+				SetDescriptorString (value.ToString (
+					CultureInfo.InvariantCulture),
+					"WM/PartOfSet");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/PartOfSet"
+		///    Content Descriptor.
+		/// </remarks>
+		public override uint DiscCount {
+			get {
+				string text = GetDescriptorString (
+					"WM/PartOfSet");
+				
+				if (text == null)
+					return 0;
+				
+				string [] texts = text.Split ('/');
+				uint value;
+				
+				if (texts.Length < 2)
+					return 0;
+				
+				return uint.TryParse (texts [1],
+					NumberStyles.Integer,
+					CultureInfo.InvariantCulture,
+					out value) ? value : 0;
+			}
+			set {
+				uint disc = Disc;
+				if (disc == 0 && value == 0) {
+					RemoveDescriptors ("WM/PartOfSet");
+					return;
+				}
+				
+				if (value != 0) {
+					SetDescriptorString (string.Format (
+						CultureInfo.InvariantCulture,
+						"{0}/{1}", disc, value),
+						"WM/PartOfSet");
+					return;
+				}
+				
+				SetDescriptorString (disc.ToString (
+					CultureInfo.InvariantCulture),
+					"WM/PartOfSet");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/Lyrics"
+		///    Content Descriptor.
+		/// </remarks>
+		public override string Lyrics {
+			get {return GetDescriptorString ("WM/Lyrics");}
+			set {SetDescriptorString (value, "WM/Lyrics");}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the
+		///    "WM/ContentGroupDescription" Content Descriptor.
+		/// </remarks>
+		public override string Grouping {
+			get {
+				return GetDescriptorString (
+					"WM/ContentGroupDescription");
+			}
+			set {
+				SetDescriptorString (value,
+					"WM/ContentGroupDescription");
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the
+		///    "WM/BeatsPerMinute" Content Descriptor.
+		/// </remarks>
+		public override uint BeatsPerMinute {
+			get {
+				foreach (ContentDescriptor desc in
+					GetDescriptors ("WM/BeatsPerMinute")) {
+						uint value = desc.ToDWord ();
+						if (value != 0)
+							return value;
+				}
+				
+				return 0;
+			}
+			set {
+				if (value == 0) {
+					RemoveDescriptors ("WM/BeatsPerMinute");
+					return;
+				}
+				
+				SetDescriptors ("WM/BeatsPerMinute",
+					new ContentDescriptor (
+						"WM/BeatsPerMinute", value));
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/Conductor"
+		///    Content Descriptor.
+		/// </remarks>
+		public override string Conductor {
+			get {return GetDescriptorString ("WM/Conductor");}
+			set {SetDescriptorString (value, "WM/Conductor");}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the copyright stored
+		///    in the ASF Content Description Object.
+		/// </remarks>
+		public override string Copyright {
+			get {return description.Copyright;}
+			set {description.Copyright = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the MusicBrainz Artist ID of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    ArtistID for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Artist Id"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzArtistId {
+			get {return GetDescriptorString ("MusicBrainz/Artist Id");}
+			set {SetDescriptorString (value, "MusicBrainz/Artist Id");}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release ID of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    ReleaseID for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Album Id"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseId {
+			get {return GetDescriptorString ("MusicBrainz/Album Id");}
+			set {SetDescriptorString (value, "MusicBrainz/Album Id");}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Artist ID of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    ReleaseArtistID for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Album Artist Id"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseArtistId {
+			get {return GetDescriptorString ("MusicBrainz/Album Artist Id");}
+			set {SetDescriptorString (value, "MusicBrainz/Album Artist Id");}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Track ID of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    TrackID for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Track Id"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzTrackId {
+			get {return GetDescriptorString ("MusicBrainz/Track Id");}
+			set {SetDescriptorString (value, "MusicBrainz/Track Id");}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Disc ID of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    DiscID for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Disc Id"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzDiscId {
+			get {return GetDescriptorString ("MusicBrainz/Disc Id");}
+			set {SetDescriptorString (value, "MusicBrainz/Disc Id");}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicIPPUID 
+		///    for the media described by the current instance or
+		///    null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicIP/PUID"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicIpId {
+			get {return GetDescriptorString ("MusicIP/PUID");}
+			set {SetDescriptorString (value, "MusicIP/PUID");}
+		}
+
+		// <summary>
+		//    Gets and sets the AmazonID of
+		//    the media described by the current instance.
+		// </summary>
+		// <value>
+		//    A <see cref="string" /> containing the AmazonID 
+		//    for the media described by the current instance or
+		//    null if no value is present.  
+		// </value>
+		// <remarks>
+		//    A definition on where to store the ASIN for
+		//    Windows Media is not currently defined
+		// </remarks>
+		//public override string AmazonId {
+		//    get { return null; }
+		//    set {}
+		//}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Status of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    ReleaseStatus for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Album Status"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseStatus {
+			get {return GetDescriptorString ("MusicBrainz/Album Status");}
+			set {SetDescriptorString (value, "MusicBrainz/Album Status");}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Type of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    ReleaseType for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Album Type"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseType {
+			get {return GetDescriptorString ("MusicBrainz/Album Type");}
+			set {SetDescriptorString (value, "MusicBrainz/Album Type");}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Country of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz 
+		///    ReleaseCountry for the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MusicBrainz/Album Release Country"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseCountry {
+			get {return GetDescriptorString ("MusicBrainz/Album Release Country");}
+			set {SetDescriptorString (value, "MusicBrainz/Album Release Country");}
+		}
+
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "WM/Picture"
+		///    Content Descriptor and Description Record.
+		/// </remarks>
+		public override IPicture [] Pictures {
+			get {
+				List<IPicture> l = new List<IPicture> ();
+				
+				foreach (ContentDescriptor descriptor in
+					GetDescriptors ("WM/Picture")) {
+					IPicture p = PictureFromData (
+						descriptor.ToByteVector ());
+					if (p != null)
+						l.Add (p);
+				}
+				
+				foreach (DescriptionRecord record in
+					metadata_library.GetRecords (0, 0,
+						"WM/Picture")) {
+					IPicture p = PictureFromData (
+						record.ToByteVector ());
+					if (p != null)
+						l.Add (p);
+				}
+				
+				return l.ToArray ();
+			}
+			set {
+				if (value == null || value.Length == 0) {
+					RemoveDescriptors ("WM/Picture");
+					metadata_library.RemoveRecords (0, 0,
+						"WM/Picture");
+					return;
+				}
+				
+				List<ByteVector> pics = new List<ByteVector> ();
+				
+				bool big_pics = false;
+				
+				foreach (IPicture pic in value) {
+					ByteVector data = PictureToData (pic);
+					pics.Add (data);
+					if (data.Count > 0xFFFF)
+						big_pics = true;
+				}
+				
+				if (big_pics) {
+					DescriptionRecord [] records =
+						new DescriptionRecord [pics.Count];
+					for (int i = 0; i < pics.Count; i ++)
+						records [i] = new DescriptionRecord (
+							0, 0, "WM/Picture", pics [i]);
+					RemoveDescriptors ("WM/Picture");
+					metadata_library.SetRecords (0, 0,
+						"WM/Picture", records);
+				} else {
+					ContentDescriptor [] descs =
+						new ContentDescriptor [pics.Count];
+					for (int i = 0; i < pics.Count; i ++)
+						descs [i] = new ContentDescriptor (
+							"WM/Picture", pics [i]);
+					metadata_library.RemoveRecords (0, 0,
+						"WM/Picture");
+					SetDescriptors ("WM/Picture", descs);
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance does not
+		///    any values. Otherwise <see langword="false" />.
+		/// </value>
+		public override bool IsEmpty {
+			get {
+				return description.IsEmpty &&
+					ext_description.IsEmpty;
+			}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			description = new ContentDescriptionObject ();
+			ext_description =
+				new ExtendedContentDescriptionObject ();
+			metadata_library.RemoveRecords (0, 0, "WM/Picture");
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Asf/UnknownObject.cs b/lib/TagLib/TagLib/Asf/UnknownObject.cs
new file mode 100644
index 0000000..cc23b3e
--- /dev/null
+++ b/lib/TagLib/TagLib/Asf/UnknownObject.cs
@@ -0,0 +1,110 @@
+//
+// UnknownObject.cs: Provides a simple and generic representation of an object
+// not identified by TagLib# so that its contents may be preserved.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Asf
+{
+	/// <summary>
+	///    This class extends <see cref="Object" /> to provide a
+	///    representation of an unknown object which can be read from and
+	///    written to disk.
+	/// </summary>
+	public class UnknownObject : Object
+	{
+		#region Private Fields
+		
+		private ByteVector data;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnknownObject" /> by reading the contents from a
+		///    specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="Asf.File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the object.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		public UnknownObject (Asf.File file, long position)
+			: base (file, position)
+		{
+			data = file.ReadBlock ((int) (OriginalSize - 24));
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    contained in the current instance.
+		/// </value>
+		public ByteVector Data {
+			get {return data;}
+			set {data = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ASF object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render ()
+		{
+			return Render (data);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/ByteVector.cs b/lib/TagLib/TagLib/ByteVector.cs
new file mode 100644
index 0000000..5ff55ee
--- /dev/null
+++ b/lib/TagLib/TagLib/ByteVector.cs
@@ -0,0 +1,2628 @@
+//
+// ByteVector.cs: represents and performs operations on variable length list of
+// bytes.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//   Aaron Bockover (abockover novell com)
+//
+// Original Source:
+//   tbytevector.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2006 Novell, Inc.
+// Copyright (C) 2002-2004 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TagLib {
+	/// <summary>
+	///    Specifies the text encoding used when converting between a <see
+	///    cref="string" /> and a <see cref="ByteVector" />.
+	/// </summary>
+	/// <remarks>
+	///    This enumeration is used by <see
+	///    cref="ByteVector.FromString(string,StringType)" /> and <see
+	///    cref="ByteVector.ToString(StringType)" />.
+	/// </remarks>
+	public enum StringType {
+		/// <summary>
+		///    The string is to be Latin-1 encoded.
+		/// </summary>
+		Latin1 = 0,
+		
+		/// <summary>
+		///    The string is to be UTF-16 encoded.
+		/// </summary>
+		UTF16 = 1,
+		
+		/// <summary>
+		///    The string is to be UTF-16BE encoded.
+		/// </summary>
+		UTF16BE = 2,
+		
+		/// <summary>
+		///    The string is to be UTF-8 encoded.
+		/// </summary>
+		UTF8 = 3,
+		
+		/// <summary>
+		///    The string is to be UTF-16LE encoded.
+		/// </summary>
+		UTF16LE = 4
+	}
+	
+	/// <summary>
+	///    This class represents and performs operations on variable length
+	///    list of <see cref="byte" /> elements.
+	/// </summary>
+	public class ByteVector : IList<byte>, IComparable<ByteVector>
+	{
+#region Private Static Fields
+		
+		/// <summary>
+		///    Contains values to use in CRC calculation.
+		/// </summary>
+		private static uint [] crc_table = new uint[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
+		};
+		
+		/// <summary>
+		///    Specifies whether or not to use a broken Latin-1
+		///    behavior.
+		/// </summary>
+		private static bool use_broken_latin1 = false;
+		
+		/// <summary>
+		///    Contains a one byte text delimiter.
+		/// </summary>
+		private static readonly ReadOnlyByteVector td1 =
+			new ReadOnlyByteVector ((int)1);
+		
+		/// <summary>
+		///    Contains a two byte text delimiter.
+		/// </summary>
+		private static readonly ReadOnlyByteVector td2 =
+			new ReadOnlyByteVector ((int)2);
+		
+		/// <summary>
+		///    Contains the last generic UTF-16 encoding read.
+		/// </summary>
+		/// <remarks>
+		///    When reading a collection of UTF-16 strings, sometimes
+		///    only the first one will contain the BOM. In that case,
+		///    this field will inform the file what encoding to use for
+		///    the second string.
+		/// </remarks>
+		private static System.Text.Encoding last_utf16_encoding =
+			System.Text.Encoding.Unicode;
+		
+#endregion
+		
+		
+		
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the internal byte list.
+		/// </summary>
+		private List<byte> data = new List<byte>();
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVector" /> with a length of zero.
+		/// </summary>
+		public ByteVector ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVector" /> by copying the values from another
+		///    instance.
+		/// </summary>
+		/// <param name="vector">
+		///    A <see cref="ByteVector" /> object containing the bytes
+		///    to be stored in the new instance.
+		/// </param>
+		public ByteVector (ByteVector vector)
+		{
+			if (vector != null)
+				this.data.AddRange (vector);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVector" /> by copying the values from a
+		///    specified <see cref="byte[]" />.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="byte[]" /> containing the bytes to be stored
+		///    in the new instance.
+		/// </param>
+		public ByteVector (params byte [] data)
+		{
+			if (data != null)
+				this.data.AddRange (data);
+		}
+		
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVector" /> by copying a specified number of
+		///    values from a specified <see cref="byte[]" />.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="byte[]" /> containing the bytes to be stored
+		///    in the new instance.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to be copied to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="length" /> is less than zero or greater
+		///    than the length of the data.
+		/// </exception>
+		public ByteVector (byte [] data, int length)
+		{
+			if (length > data.Length)
+				throw new ArgumentOutOfRangeException ("length",
+					"Length exceeds size of data.");
+			
+			if (length < 0)
+				throw new ArgumentOutOfRangeException ("length",
+					"Length is less than zero.");
+			
+			if (length == data.Length) {
+				this.data.AddRange (data);
+			} else {
+				byte [] array = new byte[length];
+				System.Array.Copy (data, 0, array, 0, length);
+				this.data.AddRange (array);
+			}
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVector" /> of specified size containing bytes
+		///    with a zeroed value.
+		/// </summary>
+		/// <param name="size">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to be stored in the new instance.
+		/// </param>
+		/// <remarks>
+		///    Each element of the new instance will have a value of
+		///    <c>0x00</c>. <see cref="ByteVector(int,byte)" /> to fill
+		///    a new instance with a specified value.
+		/// </remarks>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="size" /> is less than zero.
+		/// </exception>
+		public ByteVector (int size) : this (size, 0)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVector" /> of specified size containing bytes
+		///    of a specified value.
+		/// </summary>
+		/// <param name="size">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to be stored in the new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="byte" /> value specifying the value to be
+		///    stored in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="size" /> is less than zero.
+		/// </exception>
+		public ByteVector (int size, byte value)
+		{
+			if (size < 0)
+				throw new ArgumentOutOfRangeException ("size",
+					"Size is less than zero.");
+			
+			if(size == 0)
+				return;
+			
+			byte [] data = new byte [size];
+			
+			for (int i = 0; i < size; i ++)
+				data[i] = value;
+			
+			this.data.AddRange (data);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the data stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte[]" /> containing the data stored in the
+		///    current instance.
+		/// </value>
+		public byte [] Data {
+			get {return this.data.ToArray();}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is empty.
+		/// </value>
+		public bool IsEmpty {
+			get {return this.data.Count == 0;}
+		}
+		
+		/// <summary>
+		///    Gets the CRC-32 checksum of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the CRC-32 checksum
+		///    of the current instance.
+		/// </value>
+		public uint Checksum {
+			get {
+				uint sum = 0;
+				
+				foreach(byte b in this.data)
+					sum = (sum << 8) ^ crc_table
+						[((sum >> 24) & 0xFF) ^ b];
+				
+				return sum;
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Properties
+		
+		/// <summary>
+		///    Gets and sets whether or not to use a broken behavior for
+		///    Latin-1 strings, common to ID3v1 and ID3v2 tags.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the broken behavior is to be
+		///    used. Otherwise, <see langword="false" />.
+		/// </value>
+		/// <remarks>
+		///    <para>Many media players and taggers incorrectly treat
+		///    Latin-1 fields as "default encoding" fields. As such, a
+		///    tag may end up with Windows-1250 encoded text. While this
+		///    problem would be apparent when moving a file from one
+		///    computer to another, it would not be apparent on the
+		///    original machine. By setting this property to <see
+		///    langword="true" />, your program will behave like Windows
+		///    Media Player and others, who read tags with this broken
+		///    behavior.</para>
+		///    <para>Please note that TagLib# stores tag data in Unicode
+		///    formats at every possible instance to avoid these
+		///    problems in tags it has written.</para>
+		/// </remarks>
+		public static bool UseBrokenLatin1Behavior {
+			get {return use_broken_latin1;}
+			set {use_broken_latin1 = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" />
+		///    containing a specified range of elements from the current
+		///    instance.
+		/// </summary>
+		/// <param name="startIndex">
+		///    A <see cref="int" /> value specifying the index at which
+		///    to start copying elements from the current instance.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of
+		///    elements to copy from the current instance.
+		/// </param>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="startIndex" /> is less than zero or
+		///    greater than or equal to <see cref="Count" />. OR
+		///    <paramref name="length" /> is less than zero or greater
+		///    than the amount of available data.
+		/// </exception>
+		public ByteVector Mid (int startIndex, int length)
+		{
+			if (startIndex < 0 || startIndex > Count)
+				throw new ArgumentOutOfRangeException (
+					"startIndex");
+			
+			if (length < 0 || startIndex + length > Count)
+				throw new ArgumentOutOfRangeException (
+					"length");
+			
+			if (length == 0)
+				return new ByteVector ();
+				
+			if(startIndex + length > this.data.Count)
+				length = this.data.Count - startIndex;
+			
+			byte [] data = new byte [length];
+			
+			this.data.CopyTo (startIndex, data, 0, length);
+			
+			return data;
+		}
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" />
+		///    containing elements from the current instance starting
+		///    from a specified point.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the index at which
+		///    to start copying elements from the current instance.
+		/// </param>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="ndex" /> is less than zero or greater
+		///    than or equal to <see cref="Count" />.
+		/// </exception>
+		public ByteVector Mid (int index)
+		{
+			return Mid(index, Count - index);
+		}
+		
+		/// <summary>
+		///    Finds the first byte-aligned occurance of a pattern in
+		///    the current instance, starting at a specified position.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specifying the index in the
+		///    current instance at which to start searching.
+		/// </param>
+		/// <param name="byteAlign">
+		///    A <see cref="int"/> value specifying the byte alignment
+		///    of the pattern to search for, relative to <paramref
+		///    name="offset" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int"/> value containing the index at which
+		///    <paramref name="pattern" /> was found in the current
+		///    instance, or -1 if the pattern was not found. The
+		///    difference between the position and <paramref
+		///    name="offset" /> will be divisible by <paramref
+		///    name="byteAlign" />.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero or
+		///    <paramref name="byteAlign" /> is less than 1.
+		/// </exception>
+		public int Find (ByteVector pattern, int offset, int byteAlign)
+		{
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException (
+					"offset", "offset must be at least 0.");
+			
+			if (byteAlign < 1)
+				throw new ArgumentOutOfRangeException (
+					"byteAlign",
+					"byteAlign must be at least 1.");
+			
+			if (pattern.Count > Count - offset)
+				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.Count == 1) {
+				byte p = pattern [0];
+				for (int i = offset; i < this.data.Count;
+					i += byteAlign)
+					if (this.data [i] == p)
+						return i;
+				return -1;
+			}
+			
+			int [] last_occurrence = new int [256];
+			for (int i = 0; i < 256; ++i)
+				last_occurrence [i] = pattern.Count;
+			
+			for (int i = 0; i < pattern.Count - 1; ++i)
+				last_occurrence [pattern [i]] =
+					pattern.Count - i - 1;
+			
+			for (int i = pattern.Count - 1 + offset;
+				i < this.data.Count;
+				i += last_occurrence [this.data [i]]) {
+				int iBuffer = i;
+				int iPattern = pattern.Count - 1;
+				
+				while(iPattern >= 0 && this.data [iBuffer] ==
+					pattern [iPattern]) {
+					--iBuffer;
+					--iPattern;
+				}
+				
+				if (-1 == iPattern && (iBuffer + 1 - offset) %
+					byteAlign == 0)
+					return iBuffer + 1;
+			}
+			
+			return -1;
+		}
+		
+		/// <summary>
+		///    Finds the first occurance of a pattern in the current
+		///    instance, starting at a specified position.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specifying the index in the
+		///    current instance at which to start searching.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int"/> value containing the index at which
+		///    <paramref name="pattern" /> was found in the current
+		///    instance, or -1 if the pattern was not found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		public int Find(ByteVector pattern, int offset)
+		{
+			return Find(pattern, offset, 1);
+		}
+		
+		/// <summary>
+		///    Finds the first occurance of a pattern in the current
+		///    instance.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int"/> value containing the index at which
+		///    <paramref name="pattern" /> was found in the current
+		///    instance, or -1 if the pattern was not found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public int Find(ByteVector pattern)
+		{
+			return Find(pattern, 0, 1);
+		}
+		
+		/// <summary>
+		///    Finds the last byte-aligned occurance of a pattern in
+		///    the current instance, starting before a specified
+		///    position.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specifying the index in the
+		///    current instance at which to start searching.
+		/// </param>
+		/// <param name="byteAlign">
+		///    A <see cref="int"/> value specifying the byte alignment
+		///    of the pattern to search for, relative to <paramref
+		///    name="offset" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int"/> value containing the index at which
+		///    <paramref name="pattern" /> was found in the current
+		///    instance, or -1 if the pattern was not found. The
+		///    difference between the position and <paramref
+		///    name="offset" /> will be divisible by <paramref
+		///    name="byteAlign" />.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		public int RFind (ByteVector pattern, int offset, int byteAlign)
+		{
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException (
+					"offset");
+			
+			if (pattern.Count == 0 || pattern.Count > Count - offset)
+				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.Count == 1) {
+				byte p = pattern [0];
+				for (int i = Count - offset - 1; i >= 0;
+					i -= byteAlign)
+					if (this.data [i] == p)
+						return i;
+				return -1;
+			}
+			
+			int [] first_occurrence = new int [256];
+			
+			for (int i = 0; i < 256; ++i)
+				first_occurrence [i] = pattern.Count;
+			
+			for (int i = pattern.Count - 1; i > 0; --i)
+				first_occurrence [pattern [i]] = i;
+			
+			for (int i = Count - offset - pattern.Count; i >= 0;
+				i -= first_occurrence [this.data [i]])
+				if ((offset - i) % byteAlign == 0 &&
+					ContainsAt (pattern, i))
+					return i;
+			
+			return -1;
+		}
+		
+		/// <summary>
+		///    Finds the last occurance of a pattern in the current
+		///    instance, starting before a specified position.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specifying the index in the
+		///    current instance at which to start searching.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int"/> value containing the index at which
+		///    <paramref name="pattern" /> was found in the current
+		///    instance, or -1 if the pattern was not found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		public int RFind(ByteVector pattern, int offset)
+		{
+			return RFind (pattern, offset, 1);
+		}
+		
+		/// <summary>
+		///    Finds the last occurance of a pattern in the current
+		///    instance.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int"/> value containing the index at which
+		///    <paramref name="pattern" /> was found in the current
+		///    instance, or -1 if the pattern was not found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public int RFind(ByteVector pattern)
+		{
+			return RFind(pattern, 0, 1);
+		}
+		
+		/// <summary>
+		///    Checks whether or not a pattern appears at a specified
+		///    position in the current instance.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to check for in the current instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specifying the offset in the
+		///    current instance at which to check for the pattern.
+		/// </param>
+		/// <param name="patternOffset">
+		///    A <see cref="int"/> value specifying the position in
+		///    <paramref name="pattern" /> at which to start checking.
+		/// </param>
+		/// <param name="patternLength">
+		///    A <see cref="int"/> value specifying the number of bytes
+		///    in <paramref name="pattern" /> to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true"/> if the pattern was found at the
+		///    specified position. Otherwise, <see langword="false"/>.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public bool ContainsAt (ByteVector pattern, int offset, 
+		                        int patternOffset, int patternLength)
+		{
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			if(pattern.Count < patternLength) {
+				patternLength = pattern.Count;
+			}
+			
+			// do some sanity checking -- all of these things are 
+			// needed for the search to be valid
+			if (patternLength > this.data.Count ||
+				offset >= this.data.Count ||
+				patternOffset >= pattern.Count ||
+				patternLength <= 0 || offset < 0)
+				return false;
+			
+			// loop through looking for a mismatch
+			for (int i = 0; i < patternLength - patternOffset; i++)
+				if (this.data[i + offset] !=
+					pattern [i + patternOffset])
+					return false;
+			
+			return true;
+		}
+		
+		/// <summary>
+		///    Checks whether or not a pattern appears at a specified
+		///    position in the current instance.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to check for in the current instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specifying the offset in the
+		///    current instance at which to check for the pattern.
+		/// </param>
+		/// <param name="patternOffset">
+		///    A <see cref="int"/> value specifying the position in
+		///    <paramref name="pattern" /> at which to start checking.
+		/// </param>
+		/// <returns>
+		///    <see langword="true"/> if the pattern was found at the
+		///    specified position. Otherwise, <see langword="false"/>.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public bool ContainsAt (ByteVector pattern, int offset,
+		                        int patternOffset)
+		{
+			return ContainsAt (pattern, offset, patternOffset,
+				int.MaxValue);
+		}
+		
+		/// <summary>
+		///    Checks whether or not a pattern appears at a specified
+		///    position in the current instance.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to check for in the current instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specifying the offset in the
+		///    current instance at which to check for the pattern.
+		/// </param>
+		/// <returns>
+		///    <see langword="true"/> if the pattern was found at the
+		///    specified position. Otherwise, <see langword="false"/>.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public bool ContainsAt (ByteVector pattern, int offset)
+		{
+			return ContainsAt (pattern, offset, 0);
+		}
+		
+		/// <summary>
+		///    Checks whether or not a pattern appears at the beginning
+		///    of the current instance.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to check for in the current instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true"/> if the pattern was found at the
+		///    beginning of the current instance. Otherwise, <see
+		///    langword="false"/>.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public bool StartsWith (ByteVector pattern)
+		{
+			return ContainsAt (pattern, 0);
+		}
+		
+		/// <summary>
+		///    Checks whether or not a pattern appears at the end of the
+		///    current instance.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to check for in the current instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true"/> if the pattern was found at the
+		///    end of the current instance. Otherwise, <see
+		///    langword="false"/>.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public bool EndsWith (ByteVector pattern)
+		{
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			return ContainsAt (pattern,
+				this.data.Count - pattern.Count);
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance ends with part
+		///    of a pattern.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object containing the pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value containing the index at which
+		///    a partial match was located, or -1 if no match was found.
+		/// </returns>
+		/// <remarks>
+		///    <para>This function is useful for checking for patterns
+		///    across multiple buffers.</para>
+		/// </remarks>
+		public int EndsWithPartialMatch (ByteVector pattern)
+		{
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			if(pattern.Count > this.data.Count) {
+				return -1;
+			}
+			
+			int start_index = this.data.Count - pattern.Count;
+			
+			// 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(int i = 1; i < pattern.Count; i++) {
+				if (ContainsAt (pattern, start_index + i, 0,
+					pattern.Count - i)) {
+					return start_index + i;
+				}
+			}
+			
+			return -1;
+		}
+		
+		/// <summary>
+		///    Adds the contents of another <see cref="ByteVector" />
+		///    object to the end of the current instance.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> object containing data to add
+		///    to the end of the current instance.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void Add (ByteVector data)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			if(data != null) {
+				this.data.AddRange(data);
+			}
+		}
+		
+		/// <summary>
+		///    Adds the contents of an array to the end of the current
+		///    instance.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="byte[]"/> containing data to add to the end
+		///    of the current instance.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void Add (byte [] data)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			if(data != null)
+				this.data.AddRange(data);
+		}
+		
+		/// <summary>
+		///    Inserts the contents of another <see cref="ByteVector" />
+		///    object into the current instance.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int"/> value specifying the index at which
+		///    to insert the data.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> object containing data to
+		///    insert into the current instance.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void Insert (int index, ByteVector data)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			if (data != null)
+				this.data.InsertRange (index, data);
+		}
+		
+		/// <summary>
+		///    Inserts the contents of an array to insert into the
+		///    current instance.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int"/> value specifying the index at which
+		///    to insert the data.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="byte[]"/> containing data to insert into the
+		///    current instance.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void Insert (int index, byte [] data)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			if (data != null)
+				this.data.InsertRange (index, data);
+		}
+		
+		/// <summary>
+		///    Resizes the current instance.
+		/// </summary>
+		/// <param name="size">
+		///    A <see cref="int"/> value specifying the new size of the
+		///    current instance.
+		/// </param>
+		/// <param name="padding">
+		///    A <see cref="byte"/> object containing the padding byte
+		///    to use if the current instance is growing.
+		/// </param>
+		/// <returns>
+		///    The current instance.
+		/// </returns>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public ByteVector Resize (int size, byte padding)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			if (this.data.Count > size)
+				this.data.RemoveRange (size,
+					this.data.Count - size);
+			
+			while (this.data.Count < size)
+				this.data.Add (padding);
+			
+			return this;
+		}
+		
+		/// <summary>
+		///    Resizes the current instance.
+		/// </summary>
+		/// <param name="size">
+		///    A <see cref="int"/> value specifying the new size of the
+		///    current instance.
+		/// </param>
+		/// <returns>
+		///    The current instance.
+		/// </returns>
+		/// <remarks>
+		///    If the current instance grows, the added bytes are filled
+		///    with '0'. Use <see cref="Resize(int,byte)" /> to specify
+		///    the padding byte.
+		/// </remarks>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		/// <seealso cref="Resize(int,byte)" />
+		public ByteVector Resize (int size)
+		{
+			return Resize (size, 0);
+		}
+		
+		/// <summary>
+		///    Removes a range of data from the current instance.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the index at which
+		///    to start removing data.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="int"/> value specifying the number of bytes
+		///    to remove.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void RemoveRange (int index, int count)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			this.data.RemoveRange (index, count);
+		}
+		
+#endregion
+		
+		
+		
+#region Conversions
+		
+		/// <summary>
+		///    Converts an first four bytes of the current instance to
+		///    a <see cref="int" /> value.
+		/// </summary>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte
+		///    appears first (big endian format), or <see
+		///    langword="false" /> if the least significant byte appears
+		///    first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="int"/> value containing the value read from
+		///    the current instance.
+		/// </returns>
+		public int ToInt (bool mostSignificantByteFirst)
+		{
+			int ret = 0;
+			int last = Count > 4 ? 3 : Count - 1;
+			
+			for (int i = 0; i <= last; i++) {
+				int offset = mostSignificantByteFirst ? last-i : i;
+				ret |= (int) this[i] << (offset * 8);
+			}
+			
+			return ret;
+		}
+		
+		/// <summary>
+		///    Converts an first four bytes of the current instance to
+		///    a <see cref="int" /> value using big-endian format.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int"/> value containing the value read from
+		///    the current instance.
+		/// </returns>
+		public int ToInt ()
+		{
+			return ToInt (true);
+		}
+		
+		/// <summary>
+		///    Converts an first four bytes of the current instance to
+		///    a <see cref="uint" /> value.
+		/// </summary>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte
+		///    appears first (big endian format), or <see
+		///    langword="false" /> if the least significant byte appears
+		///    first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="uint"/> value containing the value read from
+		///    the current instance.
+		/// </returns>
+		public uint ToUInt (bool mostSignificantByteFirst)
+		{
+			uint sum = 0;
+			int last = Count > 4 ? 3 : Count - 1;
+			
+			for (int i = 0; i <= last; i++) {
+				int offset = mostSignificantByteFirst ? last-i : i;
+				sum |= (uint) this[i] << (offset * 8);
+			}
+			
+			return sum;
+		}
+		
+		/// <summary>
+		///    Converts an first four bytes of the current instance to
+		///    a <see cref="uint" /> value using big-endian format.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="uint"/> value containing the value read from
+		///    the current instance.
+		/// </returns>
+		public uint ToUInt ()
+		{
+			return ToUInt (true);
+		}
+		
+		/// <summary>
+		///    Converts an first two bytes of the current instance to a
+		///    <see cref="ushort" /> value.
+		/// </summary>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte
+		///    appears first (big endian format), or <see
+		///    langword="false" /> if the least significant byte appears
+		///    first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="ushort"/> value containing the value read
+		///    from the current instance.
+		/// </returns>
+		public ushort ToUShort (bool mostSignificantByteFirst)
+		{
+			ushort sum = 0;
+			int last = Count > 2 ? 1 : Count - 1;
+			for (int i = 0; i <= last; i++) {
+				int offset = mostSignificantByteFirst ? last-i : i;
+				sum |= (ushort)(this[i] << (offset * 8));
+			}
+			
+			return sum;
+		}
+		
+		/// <summary>
+		///    Converts an first two bytes of the current instance to
+		///    a <see cref="ushort" /> value using big-endian format.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort"/> value containing the value read
+		///    from the current instance.
+		/// </returns>
+		public ushort ToUShort ()
+		{
+			return ToUShort (true);
+		}
+		
+		/// <summary>
+		///    Converts an first eight bytes of the current instance to
+		///    a <see cref="ulong" /> value.
+		/// </summary>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte
+		///    appears first (big endian format), or <see
+		///    langword="false" /> if the least significant byte appears
+		///    first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="ulong"/> value containing the value read
+		///    from the current instance.
+		/// </returns>
+		public ulong ToULong (bool mostSignificantByteFirst)
+		{
+			ulong sum = 0;
+			int last = Count > 8 ? 7 : Count - 1;
+			for(int i = 0; i <= last; i++) {
+				int offset = mostSignificantByteFirst ? last-i : i;
+				sum |= (ulong) this[i] << (offset * 8);
+			}
+			return sum;
+		}
+		
+		/// <summary>
+		///    Converts an first eight bytes of the current instance to
+		///    a <see cref="ulong" /> value using big-endian format.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ulong"/> value containing the value read
+		///    from the current instance.
+		/// </returns>
+		public ulong ToULong ()
+		{
+			return ToULong (true);
+		}
+		
+		/// <summary>
+		///    Converts a portion of the current instance to a <see
+		///    cref="string"/> object using a specified encoding.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value indicating the encoding
+		///    to use when converting to a <see cref="string"/> object.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specify the index in the
+		///    current instance at which to start converting.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="int"/> value specify the number of bytes to
+		///    convert.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string"/> object containing the converted
+		///    text.
+		/// </returns>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero or greater
+		///    than the total number of bytes, or <paramref name="count"
+		///    /> is less than zero or greater than the number of bytes
+		///    after <paramref name="offset" />.
+		/// </exception>
+		public string ToString (StringType type, int offset, int count)
+		{
+			if (offset < 0 || offset > Count)
+				throw new ArgumentOutOfRangeException ("offset");
+			
+			if (count < 0 || count + offset > Count)
+				throw new ArgumentOutOfRangeException ("count");
+			
+			ByteVector bom = type == StringType.UTF16 &&
+				this.data.Count - offset > 1 ? Mid (offset, 2) : null;
+			
+			string s = StringTypeToEncoding (type, bom)
+				.GetString (Data, offset, count);
+			
+			// UTF16 BOM
+			if(s.Length != 0 && (s[0] == 0xfffe || s[0] == 0xfeff)) 
+				return s.Substring (1);
+			
+			return s;
+		}
+		
+		/// <summary>
+		///    Converts all data after a specified index in the current
+		///    instance to a <see cref="string"/> object using a
+		///    specified encoding.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value indicating the encoding
+		///    to use when converting to a <see cref="string"/> object.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specify the index in the
+		///    current instance at which to start converting.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string"/> object containing the converted
+		///    text.
+		/// </returns>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero or greater
+		///    than the total number of bytes.
+		/// </exception>
+		[Obsolete ("Use ToString(StringType,int,int)")]
+		public string ToString (StringType type, int offset)
+		{
+			return ToString (type, offset, Count - offset);
+		}
+		
+		/// <summary>
+		///    Converts the current instance into a <see cref="string"/>
+		///    object using a specified encoding.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string"/> object containing the converted
+		///    text.
+		/// </returns>
+		public string ToString (StringType type)
+		{
+			return ToString (type, 0, Count);
+		}
+		
+		/// <summary>
+		///    Converts the current instance into a <see cref="string"/>
+		///    object using a UTF-8 encoding.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string"/> object containing the converted
+		///    text.
+		/// </returns>
+		public override string ToString ()
+		{
+			return ToString (StringType.UTF8);
+		}
+		
+		/// <summary>
+		///    Converts the current instance into a <see cref="string[]"
+		///    /> starting at a specified offset and using a specified
+		///    encoding, assuming the values are nil separated.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value indicating the encoding
+		///    to use when converting to a <see cref="string"/> object.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specify the index in the
+		///    current instance at which to start converting.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the converted text.
+		/// </returns>
+		public string[] ToStrings (StringType type, int offset)
+		{
+			return ToStrings (type, offset, int.MaxValue);
+		}
+		
+		/// <summary>
+		///    Converts the current instance into a <see cref="string[]"
+		///    /> starting at a specified offset and using a specified
+		///    encoding, assuming the values are nil separated and
+		///    limiting it to a specified number of items.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value indicating the encoding
+		///    to use when converting to a <see cref="string"/> object.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int"/> value specify the index in the
+		///    current instance at which to start converting.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="int"/> value specifying a limit to the
+		///    number of strings to create. Once the limit has been
+		///    reached, the last string will be filled by the remainder
+		///    of the data.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the converted text.
+		/// </returns>
+		public string[] ToStrings (StringType type, int offset,
+		                           int count)
+		{
+			int chunk = 0;
+			int position = offset;
+			
+			List<string> list = new List<string> ();
+			ByteVector separator = TextDelimiter (type);
+			int align = separator.Count;
+			
+			while (chunk < count && position < Count) {
+				int start = position;
+				
+				if (chunk + 1 == count) {
+					position = offset + count;
+				} else {
+					position = Find (separator, start,
+						align);
+					
+					if (position < 0)
+						position = Count;
+				}
+				
+				int length = position - start;
+				
+				if (length == 0) {
+					list.Add (string.Empty);
+				} else {
+					string s = ToString (type, start,
+						length);
+					if (s.Length != 0 && (s[0] == 0xfffe ||
+						s[0] == 0xfeff)) { // UTF16 BOM
+						s = s.Substring (1);
+					}
+					
+					list.Add (s);
+				}
+				
+				position += align;
+			}
+			
+			return list.ToArray ();
+		}
+		
+#endregion
+		
+		
+		
+#region Operators
+		
+		/// <summary>
+		///    Determines whether two specified <see cref="ByteVector"
+		///    /> objects are equal.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <returns>
+		///    <para><see langword="true" /> if <paramref name="first"
+		///    /> and <paramref name="second" /> contain the same
+		///    data; otherwise, <see langword="false" />.</para>
+		/// </returns>
+		public static bool operator== (ByteVector first,
+		                               ByteVector second)
+		{
+			bool fnull = (object) first == null;
+			bool snull = (object) second == null;
+			if (fnull && snull)
+				return true;
+			else if (fnull || snull)
+				return false;
+			
+			return first.Equals (second);
+		}
+
+		/// <summary>
+		///    Determines whether two specified <see cref="ByteVector"
+		///    /> objects differ.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <returns>
+		///    <para><see langword="true" /> if <paramref name="first"
+		///    /> and <paramref name="second" /> contain different
+		///    data; otherwise, <see langword="false" />.</para>
+		/// </returns>
+		public static bool operator!= (ByteVector first,
+		                               ByteVector second)
+		{
+			return !(first == second);
+		}
+
+		/// <summary>
+		///    Determines whether or not one <see cref="ByteVector" />
+		///    is less than another.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <returns>
+		///    <para><see langword="true" /> if <paramref name="first"
+		///    /> is less than <paramref name="second" />; otherwise,
+		///    <see langword="false" />.</para>
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="first" /> or <paramref name="second" />
+		///    is <see langword="null" />.
+		/// </exception>
+		public static bool operator< (ByteVector first,
+		                              ByteVector second)
+		{
+			if (first == null)
+				throw new ArgumentNullException ("first");
+
+			if (second == null)
+				throw new ArgumentNullException ("second");
+			
+			return first.CompareTo (second) < 0;
+		}
+
+		/// <summary>
+		///    Determines whether or not one <see cref="ByteVector" />
+		///    is less than or equal to another.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <returns>
+		///    <para><see langword="true" /> if <paramref name="first"
+		///    /> is less than or equal to <paramref name="second" />;
+		///    otherwise, <see langword="false" />.</para>
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="first" /> or <paramref name="second" />
+		///    is <see langword="null" />.
+		/// </exception>
+		public static bool operator<= (ByteVector first,
+		                               ByteVector second)
+		{
+			if (first == null)
+				throw new ArgumentNullException ("first");
+			
+			if (second == null)
+				throw new ArgumentNullException ("second");
+			
+			return first.CompareTo (second) <= 0;
+		}
+
+		/// <summary>
+		///    Determines whether or not one <see cref="ByteVector" />
+		///    is greater than another.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <returns>
+		///    <para><see langword="true" /> if <paramref name="first"
+		///    /> is greater than <paramref name="second" />; otherwise,
+		///    <see langword="false" />.</para>
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="first" /> or <paramref name="second" />
+		///    is <see langword="null" />.
+		/// </exception>
+		public static bool operator> (ByteVector first,
+		                              ByteVector second)
+		{
+			if (first == null)
+				throw new ArgumentNullException ("first");
+
+			if (second == null)
+				throw new ArgumentNullException ("second");
+
+			return first.CompareTo (second) > 0;
+		}
+
+		/// <summary>
+		///    Determines whether or not one <see cref="ByteVector" />
+		///    is greater than or equal to another.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="ByteVector"/> to compare.
+		/// </param>
+		/// <returns>
+		///    <para><see langword="true" /> if <paramref name="first"
+		///    /> is greater than or equal to <paramref name="second"
+		///    />; otherwise, <see langword="false" />.</para>
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="first" /> or <paramref name="second" />
+		///    is <see langword="null" />.
+		/// </exception>
+		public static bool operator>= (ByteVector first,
+		                               ByteVector second)
+		{
+			if (first == null)
+				throw new ArgumentNullException ("first");
+
+			if (second == null)
+				throw new ArgumentNullException ("second");
+
+			return first.CompareTo (second) >= 0;
+		}
+		
+		/// <summary>
+		///    Creates a new <see cref="ByteVector"/> object by adding
+		///    two objects together.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="ByteVector"/> to combine.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="ByteVector"/> to combine.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="ByteVector" /> with the
+		///    contents of <paramref name="first" /> followed by the
+		///    contents of <paramref name="second" />.
+		/// </returns>
+		public static ByteVector operator+ (ByteVector first,
+		                                    ByteVector second)
+		{
+			ByteVector sum = new ByteVector(first);
+			sum.Add(second);
+			return sum;
+		}
+		
+		/// <summary>
+		///    Converts a <see cref="byte" /> to a new <see
+		///    cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="byte" /> to convert.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="ByteVector" /> containing
+		///    <paramref name="value" />.
+		/// </returns>
+		public static implicit operator ByteVector (byte value)
+		{
+			return new ByteVector (value);
+		}
+
+		/// <summary>
+		///    Converts a <see cref="byte[]" /> to a new <see
+		///    cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="byte[]" /> to convert.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="ByteVector" /> containing
+		///    the contents of <paramref name="value" />.
+		/// </returns>
+		public static implicit operator ByteVector (byte [] value)
+		{
+			return new ByteVector (value);
+		}
+
+		/// <summary>
+		///    Converts a <see cref="string" /> to a new <see
+		///    cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string" /> to convert.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="ByteVector" /> containing
+		///    the UTF-8 encoded contents of <paramref name="value" />.
+		/// </returns>
+		public static implicit operator ByteVector (string value)
+		{
+			return ByteVector.FromString (value, StringType.UTF8);
+		}
+        
+#endregion
+		
+		
+		
+#region Static Conversions
+		
+		/// <summary>
+		///    Converts a value into a data representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="int"/> value to convert into bytes.
+		/// </param>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte is
+		///    to appear first (big endian format), or <see
+		///    langword="false" /> if the least significant byte is to
+		///    appear first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromInt (int value,
+		                                   bool mostSignificantByteFirst)
+		{
+			ByteVector vector = new ByteVector();
+			for(int i = 0; i < 4; i++) {
+				int offset = mostSignificantByteFirst ? 3-i : i;
+				vector.Add ((byte)(value >> (offset * 8) & 0xFF));
+			}
+			
+			return vector;
+		}
+		
+		/// <summary>
+		///    Converts an value into a big-endian data representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="int"/> value to convert into bytes.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromInt (int value)
+		{
+			return FromInt (value, true);
+		}
+		
+		/// <summary>
+		///    Converts an unsigned value into a data representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="uint"/> value to convert into bytes.
+		/// </param>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte is
+		///    to appear first (big endian format), or <see
+		///    langword="false" /> if the least significant byte is to
+		///    appear first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromUInt (uint value,
+		                                   bool mostSignificantByteFirst)
+		{
+			ByteVector vector = new ByteVector();
+			for(int i = 0; i < 4; i++) {
+				int offset = mostSignificantByteFirst ? 3-i : i;
+				vector.Add ((byte)(value >> (offset * 8) & 0xFF));
+			}
+			
+			return vector;
+		}
+		
+		/// <summary>
+		///    Converts an unsigned value into a big-endian data
+		///    representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="uint"/> value to convert into bytes.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromUInt (uint value)
+		{
+			return FromUInt(value, true);
+		}
+		
+		/// <summary>
+		///    Converts an unsigned value into a data representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="ushort"/> value to convert into bytes.
+		/// </param>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte is
+		///    to appear first (big endian format), or <see
+		///    langword="false" /> if the least significant byte is to
+		///    appear first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromUShort (ushort value,
+		                                     bool mostSignificantByteFirst)
+		{
+			ByteVector vector = new ByteVector();
+			for(int i = 0; i < 2; i++) {
+				int offset = mostSignificantByteFirst ? 1-i : i;
+				vector.Add ((byte)(value >> (offset * 8) & 0xFF));
+			}
+			
+			return vector;
+		}
+		
+		/// <summary>
+		///    Converts an unsigned value into a big-endian data
+		///    representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="ushort"/> value to convert into bytes.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromUShort(ushort value)
+		{
+			return FromUShort (value, true);
+		}
+		
+		/// <summary>
+		///    Converts an unsigned value into a data representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="ulong"/> value to convert into bytes.
+		/// </param>
+		/// <param name="mostSignificantByteFirst">
+		///    <see langword="true" /> if the most significant byte is
+		///    to appear first (big endian format), or <see
+		///    langword="false" /> if the least significant byte is to
+		///    appear first (little endian format).
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromULong (ulong value,
+		                                    bool mostSignificantByteFirst)
+		{
+			ByteVector vector = new ByteVector();
+			for(int i = 0; i < 8; i++) {
+				int offset = mostSignificantByteFirst ? 7-i : i;
+				vector.Add ((byte)(value >> (offset * 8) & 0xFF));
+			}
+			return vector;
+		}
+		
+		/// <summary>
+		///    Converts an unsigned value into a big-endian data
+		///    representation.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="ulong"/> value to convert into bytes.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="value" />.
+		/// </returns>
+		public static ByteVector FromULong(ulong value)
+		{
+			return FromULong(value, true);
+		}
+		
+		/// <summary>
+		///    Converts an string into a encoded data representation.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string"/> object containing the text to
+		///    convert.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value specifying the encoding
+		///    to use when converting the text.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int"/> value specifying the number of
+		///    characters in <paramref name="text" /> to encoded.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="text" />.
+		/// </returns>
+		public static ByteVector FromString (string text,
+		                                     StringType type,
+		                                     int length)
+		{
+			ByteVector data = new ByteVector ();
+			
+			if (type == StringType.UTF16)
+				data.Add (new byte [] {0xff, 0xfe});
+			
+			if (text == null || text.Length == 0)
+				return data;
+			
+			if (text.Length > length)
+				text = text.Substring (0, length);
+			
+			data.Add (StringTypeToEncoding (type, data)
+				.GetBytes (text));
+			
+			return data;
+		}
+		
+		/// <summary>
+		///    Converts an string into a encoded data representation.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string"/> object containing the text to
+		///    convert.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value specifying the encoding
+		///    to use when converting the text.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="text" />.
+		/// </returns>
+		public static ByteVector FromString(string text,
+		                                    StringType type)
+		{
+			return FromString(text, type, Int32.MaxValue);
+		}
+		
+		/// <summary>
+		///    Converts an string into a encoded data representation.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string"/> object containing the text to
+		///    convert.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int"/> value specifying the number of
+		///    characters in <paramref name="text" /> to encoded.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="text" />.
+		/// </returns>
+		public static ByteVector FromString(string text, int length)
+		{
+			return FromString(text, StringType.UTF8, length);
+		}
+		
+		/// <summary>
+		///    Converts an string into a encoded data representation.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string"/> object containing the text to
+		///    convert.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the encoded
+		///    representation of <paramref name="text" />.
+		/// </returns>
+		[Obsolete("Use FromString(string,StringType)")]
+		public static ByteVector FromString(string text)
+		{
+			return FromString (text, StringType.UTF8);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" /> by
+		///    reading in the contents of a specified file.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string"/> object containing the path of the
+		///    file to read.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the contents
+		///    of the specified file.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public static ByteVector FromPath (string path)
+		{
+			byte [] tmp_out;
+			return FromPath (path, out tmp_out, false);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" /> by
+		///    reading in the contents of a specified file.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string"/> object containing the path of the
+		///    file to read.
+		/// </param>
+		/// <param name="firstChunk">
+		///    A <see cref="byte[]"/> reference to be filled with the
+		///    first data chunk from the read file.
+		/// </param>
+		/// <param name="copyFirstChunk">
+		///    A <see cref="bool"/> value specifying whether or not to
+		///    copy the first chunk of the file into <paramref
+		///    name="firstChunk" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the contents
+		///    of the specified file.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		internal static ByteVector FromPath (string path,
+		                                     out byte [] firstChunk,
+		                                     bool copyFirstChunk)
+		{
+			if (path == null)
+				throw new ArgumentNullException ("path");
+			
+			return FromFile (new File.LocalFileAbstraction (path),
+				out firstChunk, copyFirstChunk);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" /> by
+		///    reading in the contents of a specified file abstraction.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="File.IFileAbstraction"/> object containing
+		///    abstraction of the file to read.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the contents
+		///    of the specified file.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public static ByteVector FromFile (File.IFileAbstraction
+		                                   abstraction)
+		{
+			byte [] tmp_out;
+			return FromFile (abstraction, out tmp_out, false);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" /> by
+		///    reading in the contents of a specified file abstraction.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="File.IFileAbstraction"/> object containing
+		///    abstraction of the file to read.
+		/// </param>
+		/// <param name="firstChunk">
+		///    A <see cref="byte[]"/> reference to be filled with the
+		///    first data chunk from the read file.
+		/// </param>
+		/// <param name="copyFirstChunk">
+		///    A <see cref="bool"/> value specifying whether or not to
+		///    copy the first chunk of the file into <paramref
+		///    name="firstChunk" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the contents
+		///    of the specified file.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		internal static ByteVector FromFile (File.IFileAbstraction
+		                                     abstraction,
+		                                     out byte [] firstChunk,
+		                                     bool copyFirstChunk)
+		{
+			if (abstraction == null)
+				throw new ArgumentNullException ("abstraction");
+			
+			System.IO.Stream stream = abstraction.ReadStream;
+			ByteVector output = FromStream (stream, out firstChunk,
+				copyFirstChunk);
+			abstraction.CloseStream (stream);
+			return output;
+		}
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" /> by
+		///    reading in the contents of a specified stream.
+		/// </summary>
+		/// <param name="stream">
+		///    A <see cref="System.IO.Stream"/> object containing
+		///    the stream to read.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the contents
+		///    of the specified stream.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="stream" /> is <see langword="null" />.
+		/// </exception>
+		public static ByteVector FromStream (System.IO.Stream stream)
+		{
+			byte [] tmp_out;
+			return FromStream (stream, out tmp_out, false);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of <see cref="ByteVector" /> by
+		///    reading in the contents of a specified stream.
+		/// </summary>
+		/// <param name="stream">
+		///    A <see cref="System.IO.Stream"/> object containing
+		///    the stream to read.
+		/// </param>
+		/// <param name="firstChunk">
+		///    A <see cref="byte[]"/> reference to be filled with the
+		///    first data chunk from the read stream.
+		/// </param>
+		/// <param name="copyFirstChunk">
+		///    A <see cref="bool"/> value specifying whether or not to
+		///    copy the first chunk of the stream into <paramref
+		///    name="firstChunk" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the contents
+		///    of the specified stream.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="stream" /> is <see langword="null" />.
+		/// </exception>
+		internal static ByteVector FromStream (System.IO.Stream stream,
+		                                       out byte [] firstChunk,
+		                                       bool copyFirstChunk)
+		{
+			ByteVector vector = new ByteVector();
+			byte [] bytes = new byte[4096];
+			int read_size = bytes.Length;
+			int bytes_read = 0;
+			bool set_first_chunk = false;
+			
+			firstChunk = null;
+			
+			while (true) {
+				Array.Clear(bytes, 0, bytes.Length);
+				int n = stream.Read(bytes, 0, read_size);
+				vector.Add(bytes);
+				bytes_read += n;
+				
+				if (!set_first_chunk) {
+					if (copyFirstChunk) {
+						if(firstChunk == null ||
+							firstChunk.Length != read_size) {
+							firstChunk = new byte [read_size];
+						}
+						
+						Array.Copy (bytes, 0, firstChunk, 0, n);
+					}
+					set_first_chunk = true;
+				}
+				
+				if ((bytes_read == stream.Length && stream.Length > 0) || 
+					(n < read_size && stream.Length <= 0)) {
+					break;
+				}
+			}
+			
+			if (stream.Length > 0 && vector.Count != stream.Length) {
+				vector.Resize ((int)stream.Length);
+			}
+			
+			return vector;
+		}
+		
+#endregion
+		
+		
+		
+#region Utilities
+		
+		/// <summary>
+		///    Gets the text delimiter for nil separated string lists of
+		///    a specified encoding.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value specifying the encoding
+		///    to use.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the text
+		///    delimiter.
+		/// </returns>
+		public static ByteVector TextDelimiter (StringType type)
+		{
+			return type == StringType.UTF16 ||
+				type == StringType.UTF16BE ||
+				type == StringType.UTF16LE ? td2 : td1;
+		}
+		
+		/// <summary>
+		///    Gets the <see cref="Encoding" /> to use for a specified
+		///    encoding.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType"/> value specifying encoding to
+		///    use.
+		/// </param>
+		/// <param name="bom">
+		///    A <see cref="ByteVector"/> object containing the first
+		///    two bytes of the data to convert if <paramref
+		///    name="type" /> equals <see cref="StringType.UTF16" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Encoding" /> object capable of encoding
+		///    and decoding text with the specified type.
+		/// </returns>
+		/// <remarks>
+		///    <paramref name="bom" /> is used to determine whether the
+		///    encoding is big or little endian. If it does not contain
+		///    BOM data, the previously used endian format is used.
+		/// </remarks>
+		private static Encoding StringTypeToEncoding (StringType type,
+		                                              ByteVector bom)
+		{
+			switch(type) {
+			case StringType.UTF16:
+				// If we have a BOM, return the appropriate
+				// encoding. Otherwise, assume we're reading
+				// from a string that was already identified. In
+				// that case, the encoding will be stored as
+				// last_utf16_encoding.
+				
+				if (bom == null)
+					return last_utf16_encoding;
+				
+				if (bom [0] == 0xFF && bom [1] == 0xFE)
+					return last_utf16_encoding =
+						Encoding.Unicode;
+				
+				if (bom [1] == 0xFF && bom [0] == 0xFE)
+					return last_utf16_encoding =
+						Encoding.BigEndianUnicode;
+				
+				return last_utf16_encoding;
+				
+			case StringType.UTF16BE:
+				return Encoding.BigEndianUnicode;
+				
+			case StringType.UTF8:
+				return Encoding.UTF8;
+				
+			case StringType.UTF16LE:
+				return Encoding.Unicode;
+			}
+			
+			if (use_broken_latin1)
+				return Encoding.Default;
+			
+			try {
+				return Encoding.GetEncoding ("latin1");
+			} catch (ArgumentException) {
+				return Encoding.UTF8;
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region System.Object
+		
+		/// <summary>
+		///    Determines whether another object is equal to the current
+		///    instance.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="object"/> to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="other"/> is not
+		///    <see langword="null" />, is of type <see
+		///    cref="ByteVector" />, and is equal to the current
+		///    instance; otherwise <see langword="false" />.
+		/// </returns>
+		public override bool Equals (object other)
+		{
+			if (!(other is ByteVector))
+				return false;
+			
+			return Equals ((ByteVector) other);
+		}
+		
+		/// <summary>
+		///    Determines whether another <see cref="ByteVector"/>
+		///    object is equal to the current instance.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="ByteVector"/> object to compare to the
+		///    current instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="other"/> is not
+		///    <see langword="null" /> and equal to the current instance;
+		///    otherwise <see langword="false" />.
+		/// </returns>
+		public bool Equals (ByteVector other)
+		{
+			return CompareTo (other) == 0;
+		}
+		
+		/// <summary>
+		///    Gets the hash value for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value equal to the CRC checksum of
+		///    the current instance.
+		/// </returns>
+		public override int GetHashCode ()
+		{
+			unchecked {return (int) Checksum;}
+		}
+		
+#endregion
+		
+		
+		
+#region IComparable<T>
+		
+		/// <summary>
+		///    Compares the current instance to another to determine if
+		///    their order.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="ByteVector" /> object to compare to the
+		///    current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> which is less than zero if the
+		///    current instance is less than <paramref name="other" />,
+		///    zero if it is equal to <paramref name="other" />, and
+		///    greater than zero if the current instance is greater than
+		///    <paramref name="other" />.
+		/// </returns>
+		public int CompareTo (ByteVector other)
+		{
+			if ((object) other == null)
+				throw new ArgumentNullException ("other");
+		
+			int diff = Count - other.Count;
+		
+			for(int i = 0; diff == 0 && i < Count; i ++)
+				diff = this [i] - other [i];
+		
+			return diff;
+		}
+		
+#endregion
+		
+		
+		
+#region IEnumerable<T>
+
+		/// <summary>
+		///    Gets an enumerator for enumerating through the the bytes
+		///    in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the contents of the current instance.
+		/// </returns>
+		public IEnumerator<byte> GetEnumerator()
+		{
+			return this.data.GetEnumerator();
+		}
+
+		IEnumerator IEnumerable.GetEnumerator()
+		{
+			return this.data.GetEnumerator();
+		}
+		
+#endregion
+		
+		
+		
+#region ICollection<T>
+		
+		/// <summary>
+		///    Clears the current instance.
+		/// </summary>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void Clear()
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException ("Cannot edit readonly objects.");
+		
+			this.data.Clear();
+		}
+		
+		/// <summary>
+		///    Adds a single byte to the end of the current instance.
+		/// </summary>
+		/// <param name="item">
+		///    A <see cref="byte" /> to add to the current instance.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void Add (byte item)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+		
+			this.data.Add(item);
+		}
+		
+		/// <summary>
+		///    Removes the first occurance of a <see cref="byte" /> from
+		///    the current instance.
+		/// </summary>
+		/// <param name="item">
+		///    A <see cref="byte"/> to remove from the current instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the value was removed;
+		///    otherwise the value did not appear in the current
+		///    instance and <see langword="false" /> is returned.
+		/// </returns>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public bool Remove (byte item)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			return this.data.Remove(item);
+		}
+		
+		/// <summary>
+		///    Copies the current instance to a <see cref="byte[]"/>
+		///    starting at a specified index.
+		/// </summary>
+		/// <param name="array">
+		///    A <see cref="byte[]" /> to copy to.
+		/// </param>
+		/// <param name="arrayIndex">
+		///    A <see cref="int" /> value indicating the index in
+		///    <paramref name="array" /> at which to start copying.
+		/// </param>
+		public void CopyTo (byte [] array, int arrayIndex)
+		{
+			this.data.CopyTo (array, arrayIndex);
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance contains a
+		///    specified byte.
+		/// </summary>
+		/// <param name="item">
+		///    A <see cref="byte" /> value to look for in the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the value could be found;
+		///    otherwise <see langword="false" />.
+		/// </returns>
+		public bool Contains (byte item)
+		{
+			return this.data.Contains (item);
+		}
+		
+		/// <summary>
+		///    Gets the number of elements in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of bytes
+		///    in the current instance.
+		/// </value>
+		public int Count {
+			get {return this.data.Count;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is synchronized.
+		/// </summary>
+		/// <value>
+		///    Always <see langword="false" />.
+		/// </value>
+		public bool IsSynchronized {
+			get {return false;}
+		}
+		
+		/// <summary>
+		///    Gets the object that can be used to synchronize the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="object" /> that can be used to synchronize
+		///    the current instance.
+		/// </value>
+		public object SyncRoot {
+			get {return this;}
+		}
+		
+#endregion
+		
+		
+		
+#region IList<T>
+		
+		/// <summary>
+		///    Removes the byte at the specified index.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the position at
+		///    which to remove a byte.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void RemoveAt (int index)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			this.data.RemoveAt(index);
+		}
+		
+		/// <summary>
+		///    Inserts a single byte into the current instance at a
+		//     specified index.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the position at
+		///    which to insert the value.
+		/// </param>
+		/// <param name="item">
+		///    A <see cref="byte"/> value to insert into the current
+		///    instance.
+		/// </param>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public void Insert (int index, byte item)
+		{
+			if (IsReadOnly)
+				throw new NotSupportedException (
+					"Cannot edit readonly objects.");
+			
+			this.data.Insert(index, item);
+		}
+		
+		/// <summary>
+		///    Gets the index of the first occurance of a value.
+		/// </summary>
+		/// <param name="item">
+		///    A <see cref="byte" /> to find in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value containing the first index
+		///    at which the value was found, or -1 if it was not found.
+		/// </returns>
+		public int IndexOf (byte item)
+		{
+			return this.data.IndexOf (item);
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is read-only.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance is
+		///    read-only; otherwise <see langword="false" />.
+		/// </value>
+		public virtual bool IsReadOnly {
+			get {return false;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance has a fixed
+		///    size.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance has a
+		///    fixed size; otherwise <see langword="false" />.
+		/// </value>
+		public virtual bool IsFixedSize {
+			get {return false;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the value as a specified index.
+		/// </summary>
+		/// <exception cref="NotSupportedException">
+		///    The current instance is read-only.
+		/// </exception>
+		public byte this[int index] {
+			get {return this.data[index]; }
+			set {
+				if (IsReadOnly)
+					throw new NotSupportedException (
+						"Cannot edit readonly objects.");
+				
+				data[index] = value;
+			}
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/ByteVectorList.cs b/lib/TagLib/TagLib/ByteVectorList.cs
new file mode 100644
index 0000000..379fa9a
--- /dev/null
+++ b/lib/TagLib/TagLib/ByteVectorList.cs
@@ -0,0 +1,281 @@
+//
+// ByteVectorList.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//   Aaron Bockover (abockover novell com)
+//
+// Original Source:
+//   tbytevectorlist.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2006 Novell, Inc.
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace TagLib {
+	/// <summary>
+	///    This class extends <see cref="T:TagLib.ListBase`1"/> to represent
+	///    a collection of <see cref="ByteVector" /> objects.
+	/// </summary>
+	[ComVisible(false)]
+	public class ByteVectorCollection : ListBase<ByteVector>
+	{
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVectorCollection" /> with no contents.
+		/// </summary>
+		public ByteVectorCollection ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVectorCollection" /> with specified contents.
+		/// </summary>
+		/// <param name="list">
+		///   A <see cref="T:System.Collections.Generic.IEnumerable`1"
+		///   /> containing <see cref="ByteVector" /> objects to add to
+		///   the current instance.
+		/// </param>
+		public ByteVectorCollection(IEnumerable<ByteVector> list)
+		{
+			if (list != null)
+				Add (list);
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ByteVectorCollection" /> with specified contents.
+		/// </summary>
+		/// <param name="list">
+		///   A <see cref="ByteVector[]" /> containing objects to add to
+		///   the current instance.
+		/// </param>
+		public ByteVectorCollection (params ByteVector[] list)
+		{
+			if (list != null)
+				Add (list);
+		}
+		
+		/// <summary>
+		///    Performs a sorted insert of a <see cref="ByteVector" />
+		///    object into the current instance, optionally only adding
+		///    if the item is unique.
+		/// </summary>
+		/// <param name="item">
+		///    A <see cref="ByteVector" /> object to add to the current
+		///    instance.
+		/// </param>
+		/// <param name="unique">
+		///    If <see langword="true" />, the object will only be added
+		///    if an identical value is not already contained in the
+		///    current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="item" /> is <see langword="null" />.
+		/// </exception>
+		public override void SortedInsert (ByteVector item, bool unique)
+		{
+			if (item == null)
+				throw new ArgumentNullException ("item");
+			
+			// FIXME: This is not used, but if it is a faster
+			// method could be used.
+			int i = 0;
+			for(; i < Count; i++) {
+				if (item == this[i] && unique)
+					return;
+
+				if (item >= this[i])
+					break;
+			}
+
+			Insert (i + 1, item);
+		}
+		
+		/// <summary>
+		///    Converts the current instance to a <see cref="ByteVector"
+		///    /> by joining the contents together with a specified
+		///    separator.
+		/// </summary>
+		/// <param name="separator">
+		///    A <see cref="ByteVector"/> object to separate the
+		///    combined contents of the current instance.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="ByteVector"/> object containing the
+		///    joined contents of the current instance.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="separator" /> is <see langword="null" />.
+		/// </exception>
+		public ByteVector ToByteVector (ByteVector separator)
+		{
+			if (separator == null)
+				throw new ArgumentNullException ("separator");
+			
+			ByteVector vector = new ByteVector();
+			
+			for(int i = 0; i < Count; i++) {
+				if(i != 0 && separator.Count > 0)
+					vector.Add(separator);
+				
+				vector.Add(this[i]);
+			}
+			
+			return vector;
+		}
+		
+		/// <summary>
+		///    Splits a <see cref="ByteVector" /> object using a
+		///    pattern.
+		/// </summary>
+		/// <param name="vector">
+		///    A <see cref="ByteVector"/> object to split.
+		/// </param>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object to use to split
+		///    <paramref name="vector" /> with.
+		/// </param>
+		/// <param name="byteAlign">
+		///    A <see cref="int" /> specifying the byte align to use
+		///    when splitting. In order to split when a pattern is
+		///    encountered, the index at which it is found must be
+		//     divisible by <paramref name="byteAlign" />.
+		/// </param>
+		/// <param name="max">
+		///    A <see cref="int" /> value specifying the maximum number
+		///    of objects to return, or zero to not to limit the number.
+		///    If that that number is reached, the last value will
+		///    contain the remainder of the file even if it contains
+		///    more instances of <paramref name="pattern" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    the split contents of the current instance.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="vector" /> or <paramref name="pattern" />
+		///    is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="byteAlign" /> is less than 1.
+		/// </exception>
+		public static ByteVectorCollection Split (ByteVector vector,
+		                                          ByteVector pattern,
+		                                          int byteAlign,
+		                                          int max)
+		{
+			if (vector == null)
+				throw new ArgumentNullException ("vector");
+			
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			if (byteAlign < 1)
+				throw new ArgumentOutOfRangeException (
+					"byteAlign",
+					"byteAlign must be at least 1.");
+			
+			ByteVectorCollection list = new ByteVectorCollection ();
+			int previous_offset = 0;
+			
+			for (int offset = vector.Find(pattern, 0, byteAlign);
+				offset != -1 && (max < 1 ||
+					max > list.Count + 1);
+				offset = vector.Find (pattern,
+					offset + pattern.Count, byteAlign)) {
+				list.Add (vector.Mid (previous_offset,
+					offset - previous_offset));
+				previous_offset = offset + pattern.Count;
+			}
+			
+			if (previous_offset < vector.Count)
+				list.Add (vector.Mid (previous_offset,
+					vector.Count - previous_offset));
+			
+			return list;
+		}
+		
+		/// <summary>
+		///    Splits a <see cref="ByteVector" /> object using a
+		///    pattern.
+		/// </summary>
+		/// <param name="vector">
+		///    A <see cref="ByteVector"/> object to split.
+		/// </param>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object to use to split
+		///    <paramref name="vector" /> with.
+		/// </param>
+		/// <param name="byteAlign">
+		///    A <see cref="int" /> specifying the byte align to use
+		///    when splitting. In order to split when a pattern is
+		///    encountered, the index at which it is found must be
+		//     divisible by <paramref name="byteAlign" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    the split contents of the current instance.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="vector" /> or <paramref name="pattern" />
+		///    is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="byteAlign" /> is less than 1.
+		/// </exception>
+		public static ByteVectorCollection Split (ByteVector vector,
+		                                          ByteVector pattern,
+		                                          int byteAlign)
+		{
+			return Split(vector, pattern, byteAlign, 0);
+		}
+		
+		/// <summary>
+		///    Splits a <see cref="ByteVector" /> object using a
+		///    pattern.
+		/// </summary>
+		/// <param name="vector">
+		///    A <see cref="ByteVector"/> object to split.
+		/// </param>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector"/> object to use to split
+		///    <paramref name="vector" /> with.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    the split contents of the current instance.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="vector" /> or <paramref name="pattern" />
+		///    is <see langword="null" />.
+		/// </exception>
+		public static ByteVectorCollection Split (ByteVector vector,
+		                                          ByteVector pattern)
+		{
+			return Split(vector, pattern, 1);
+		}
+	}
+}
+
diff --git a/lib/TagLib/TagLib/CombinedTag.cs b/lib/TagLib/TagLib/CombinedTag.cs
new file mode 100644
index 0000000..9cda5ed
--- /dev/null
+++ b/lib/TagLib/TagLib/CombinedTag.cs
@@ -0,0 +1,1513 @@
+//
+// CombinedTag.cs: Combines a collection of tags so that they behave as one.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// 
+// 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
+//
+
+using System.Collections.Generic;
+
+namespace TagLib {
+	/// <summary>
+	///    This class combines a collection of tags so that they behave as
+	///    one.
+	/// </summary>
+	public class CombinedTag : Tag
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains tags to be combined.
+		/// </summary>
+		private List<Tag> tags;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CombinedTag" /> with no internal tags.
+		/// </summary>
+		/// <remarks>
+		///    You can set the tags in the new instance later using
+		///    <see cref="SetTags" />.
+		/// </remarks>
+		public CombinedTag ()
+		{
+			this.tags = new List<Tag> ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CombinedTag" /> with a specified collection of
+		///    tags.
+		/// </summary>
+		/// <param name="tags">
+		///    A <see cref="Tag[]" /> containing a collection of tags to
+		///    combine in the new instance.
+		/// </param>
+		public CombinedTag (params Tag [] tags)
+		{
+			this.tags = new List<Tag> (tags);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the tags combined in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="Tag[]" /> containing the tags combined in
+		///    the current instance.
+		/// </value>
+		public virtual Tag [] Tags {
+			get {return tags.ToArray ();}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Sets the child tags to combine in the current instance.
+		/// </summary>
+		/// <param name="tags">
+		///    A <see cref="Tag[]" /> containing the tags to combine.
+		/// </param>
+		public void SetTags (params Tag [] tags)
+		{
+			this.tags.Clear ();
+			this.tags.AddRange (tags);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Inserts a tag into the collection of tags in the current
+		///    instance.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the index at which
+		///    to insert the tag.
+		/// </param>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to insert into the collection
+		///    of tags.
+		/// </param>
+		/// <exception cref="System.ArgumentOutOfRangeException">
+		///    <paramref name="index" /> is less than zero or greater
+		///    than the count.
+		/// </exception>
+		protected void InsertTag (int index, Tag tag)
+		{
+			this.tags.Insert (index, tag);
+		}
+		
+		/// <summary>
+		///    Adds a tag at the end of the collection of tags in the
+		///    current instance.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to add to the collection of
+		///    tags.
+		/// </param>
+		protected void AddTag (Tag tag)
+		{
+			this.tags.Add (tag);
+		}
+		
+		/// <summary>
+		///    Removes a specified tag from the collection in the
+		///    current instance.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to remove from the
+		///    collection.
+		/// </param>
+		protected void RemoveTag (Tag tag)
+		{
+			this.tags.Remove (tag);
+		}
+		
+		/// <summary>
+		///    Clears the tag collection in the current instance.
+		/// </summary>
+		protected void ClearTags ()
+		{
+			this.tags.Clear ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Overrides
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" />
+		///    containing the tag types contained in the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    This value contains a bitwise combined value from all the
+		///    child tags.
+		/// </remarks>
+		/// <seealso cref="Tag.TagTypes" />
+		public override TagTypes TagTypes {
+			get {
+				TagTypes types = TagTypes.None;
+				foreach (Tag tag in tags)
+					if (tag != null)
+						types |= tag.TagTypes;
+				
+				return types;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Title" />
+		public override string Title {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Title;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Title = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Performers" />
+		public override string [] Performers {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string [] value = tag.Performers;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string [] {};
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Performers = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.PerformersSort" />
+		public override string[] PerformersSort {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.PerformersSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.PerformersSort = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the band or artist who
+		///    is credited in the creation of the entire album or
+		///    collection containing the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the band or artist who is credited in the creation
+		///    of the entire album or collection containing the media
+		///    described by the current instance or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AlbumArtistsSort" />
+		public override string[] AlbumArtistsSort {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.AlbumArtistsSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.AlbumArtistsSort = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AlbumArtists" />
+		public override string [] AlbumArtists {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string [] value = tag.AlbumArtists;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string [] {};
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.AlbumArtists = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Composers" />
+		public override string [] Composers {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string [] value = tag.Composers;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string [] {};
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Composers = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the composer of the 
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the composers of the media described by the 
+		///    current instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.ComposersSort" />
+		public override string[] ComposersSort {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.ComposersSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.ComposersSort = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the Track Title of the 
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names
+		///    for the Track Title of the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.TitleSort" />
+		public override string TitleSort {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.TitleSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.TitleSort = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the Album Title of the 
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names
+		///    for the Title of the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AlbumSort" />
+		public override string AlbumSort {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.AlbumSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.AlbumSort = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Album" />
+		public override string Album {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Album;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Album = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Comment" />
+		public override string Comment {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Comment;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Comment = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Genres" />
+		public override string [] Genres {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string [] value = tag.Genres;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string [] {};
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Genres = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Year" />
+		public override uint Year {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					uint value = tag.Year;
+					
+					if (value != 0)
+						return value;
+				}
+				
+				return 0;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Year = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Track" />
+		public override uint Track {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					uint value = tag.Track;
+					
+					if (value != 0)
+						return value;
+				}
+				
+				return 0;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Track = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.TrackCount" />
+		public override uint TrackCount {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					uint value = tag.TrackCount;
+					
+					if (value != 0)
+						return value;
+				}
+				
+				return 0;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.TrackCount = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Disc" />
+		public override uint Disc {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					uint value = tag.Disc;
+					
+					if (value != 0)
+						return value;
+				}
+				
+				return 0;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Disc = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.DiscCount" />
+		public override uint DiscCount {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					uint value = tag.DiscCount;
+					
+					if (value != 0)
+						return value;
+				}
+				
+				return 0;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.DiscCount = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Lyrics" />
+		public override string Lyrics {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Lyrics;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Lyrics = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Grouping" />
+		public override string Grouping {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Grouping;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Grouping = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.BeatsPerMinute" />
+		public override uint BeatsPerMinute {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					uint value = tag.BeatsPerMinute;
+					
+					if (value != 0)
+						return value;
+				}
+				
+				return 0;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.BeatsPerMinute = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Conductor" />
+		public override string Conductor {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Conductor;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Conductor = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Copyright" />
+		public override string Copyright {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Copyright;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.Copyright = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the MusicBrainz Artist ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ArtistID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzArtistId" />
+		public override string MusicBrainzArtistId {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzArtistId;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzArtistId = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseId" />
+		public override string MusicBrainzReleaseId {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseId;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzReleaseId = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Artist ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseArtistID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseArtistId" />
+		public override string MusicBrainzReleaseArtistId {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseArtistId;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzReleaseArtistId = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Track ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    TrackID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzTrackId" />
+		public override string MusicBrainzTrackId {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzTrackId;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzTrackId = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Disc ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    DiscID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzDiscId" />
+		public override string MusicBrainzDiscId {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzDiscId;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzDiscId = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicIP PUID
+		///    for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicIpId" />
+		public override string MusicIpId {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicIpId;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicIpId = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the Amazon ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the Amazon Id
+		///    for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AmazonId" />
+		public override string AmazonId {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.AmazonId;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.AmazonId = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Status.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseStatus for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseStatus" />
+		public override string MusicBrainzReleaseStatus {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseStatus;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzReleaseStatus = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Type.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseType for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseType" />
+		public override string MusicBrainzReleaseType {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseType;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzReleaseType = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Country.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseCountry for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseCountry" />
+		public override string MusicBrainzReleaseCountry {
+			get {
+				foreach (Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseCountry;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			
+			set {
+				foreach (Tag tag in tags)
+					if (tag != null)
+						tag.MusicBrainzReleaseCountry = value;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child tags are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in each child
+		///    tag.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Pictures" />
+		public override IPicture [] Pictures {
+			get {
+				foreach(Tag tag in tags) {
+					if (tag == null)
+						continue;
+					
+					IPicture [] value = tag.Pictures;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return base.Pictures;
+			}
+			
+			set {
+				foreach(Tag tag in tags)
+					if(tag != null)
+						tag.Pictures = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if all the child tags are empty.
+		///    Otherwise <see langword="false" />.
+		/// </value>
+		/// <seealso cref="Tag.IsEmpty" />
+		public override bool IsEmpty {
+			get {
+				foreach (Tag tag in tags)
+					if (tag.IsEmpty)
+						return true;
+				
+				return false;
+			}
+		}
+		
+		/// <summary>
+		///    Clears all of the child tags.
+		/// </summary>
+		public override void Clear ()
+		{
+			foreach (Tag tag in tags)
+				tag.Clear ();
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/CorruptFileException.cs b/lib/TagLib/TagLib/CorruptFileException.cs
new file mode 100644
index 0000000..ed31544
--- /dev/null
+++ b/lib/TagLib/TagLib/CorruptFileException.cs
@@ -0,0 +1,168 @@
+//
+// CorruptFileException.cs:
+//
+// Author:
+//   Aaron Bockover (abockover novell com)
+//
+// Original Source:
+//   Entagged#
+//
+// Copyright (C) 2006 Novell, Inc.
+// 
+// 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
+//
+
+using System;
+using System.Runtime.Serialization;
+
+namespace TagLib {
+	/// <summary>
+	///    This class extends <see cref="Exception" /> and is used to
+	///    indicate that a file or tag is corrupt.
+	/// </summary>
+	/// <remarks>
+	///    This exception will be thrown if invalid data interferes with the
+	///    reading of the file or tag. One common example is in the (legal)
+	///    downloading of media files with BitTorrent, in which case large
+	///    portions of the file will contain zeroed bytes.
+	/// </remarks>
+	/// <example>
+	///    <para>Catching an exception when creating a <see
+	///    cref="File" />.</para>
+	///    <code lang="C#">
+	/// using System;
+	/// using TagLib;
+	///
+	/// public class ExceptionTest
+	/// {
+	/// 	public static void Main ()
+	/// 	{
+	/// 		try {
+	/// 			File file = File.Create ("partial.mp3"); // Partial download.
+	/// 		} catch (CorruptFileException e) {
+	/// 			Console.WriteLine ("That file is corrupt: {0}", e.ToString ());
+	/// 		}
+	///	}
+	/// }
+	///    </code>
+	///    <code lang="C++">
+	/// #using &lt;System.dll>
+	/// #using &lt;taglib-sharp.dll>
+	/// 
+	/// using System;
+	/// using TagLib;
+	///
+	/// void main ()
+	/// {
+	/// 	try {
+	/// 		File file = File::Create ("partial.mp3"); // Partial download.
+	/// 	} catch (CorruptFileException^ e) {
+	/// 		Console::WriteLine ("That file is corrupt: {0}", e);
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="VB">
+	/// Imports System
+	/// Imports TagLib
+	///
+	/// Public Class ExceptionTest
+	/// 	Public Shared Sub Main ()
+	/// 		Try
+	/// 			file As File = File.Create ("partial.mp3") ' Partial download.
+	/// 		Catch e As CorruptFileException
+	/// 			Console.WriteLine ("That file is corrupt: {0}", e.ToString ());
+	/// 		End Try
+	///	End Sub
+	/// End Class
+	///    </code>
+	///    <code lang="Boo">
+	/// import System
+	/// import TagLib
+	///
+	/// try:
+	/// 	file As File = File.Create ("partial.mp3") # Partial download.
+	/// catch e as CorruptFileException:
+	/// 	Console.WriteLine ("That file is corrupt: {0}", e.ToString ());
+	///    </code>
+	/// </example>
+	[Serializable]
+	public class CorruptFileException : Exception
+	{
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CorruptFileException" /> with a specified
+		///    message.
+		/// </summary>
+		/// <param name="message">
+		///    A <see cref="string" /> containing a message explaining
+		///    the reason for the exception.
+		/// </param>
+		public CorruptFileException (string message) : base(message)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CorruptFileException" /> with the default
+		///    values.
+		/// </summary>
+		public CorruptFileException () : base()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CorruptFileException" /> with a specified
+		///    message containing a specified exception.
+		/// </summary>
+		/// <param name="message">
+		///    A <see cref="string" /> containing a message explaining
+		///    the reason for the exception.
+		/// </param>
+		/// <param name="innerException">
+		///    A <see cref="Exception" /> object to be contained in the
+		///    new exception. For example, previously caught exception.
+		/// </param>
+		public CorruptFileException (string message,
+		                             Exception innerException)
+			: base (message, innerException)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CorruptFileException" /> from a specified
+		///    serialization info and streaming context.
+		/// </summary>
+		/// <param name="info">
+		///    A <see cref="SerializationInfo" /> object containing the
+		///    serialized data to be used for the new instance.
+		/// </param>
+		/// <param name="context">
+		///    A <see cref="StreamingContext" /> object containing the
+		///    streaming context information for the new instance.
+		/// </param>
+		/// <remarks>
+		///    This constructor is implemented because <see
+		///    cref="CorruptFileException" /> implements the <see
+		///    cref="ISerializable" /> interface.
+		/// </remarks>
+		protected CorruptFileException (SerializationInfo info,
+		                                StreamingContext context)
+			: base(info, context)
+		{
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Debugger.cs b/lib/TagLib/TagLib/Debugger.cs
new file mode 100644
index 0000000..9825cc8
--- /dev/null
+++ b/lib/TagLib/TagLib/Debugger.cs
@@ -0,0 +1,144 @@
+//
+// Debugger.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib {
+	internal static class Debugger
+	{
+		public delegate void DebugMessageSentHandler (string message);
+
+		public static event DebugMessageSentHandler DebugMessageSent;
+
+		public static void Debug (string message)
+		{
+			if (DebugMessageSent != null)
+				DebugMessageSent (message);
+		}
+
+		public static void DumpHex (ByteVector data)
+		{
+			DumpHex (data.Data);
+		}
+		
+		public static void DumpHex (byte [] data)
+		{
+		        int cols = 16;
+		        int rows = data.Length / cols +
+		        	(data.Length % cols != 0 ? 1 : 0);
+			
+			for (int row = 0; row < rows; row ++) {
+				for (int col = 0; col < cols; col ++) {
+					if (row == rows - 1 &&
+						data.Length % cols != 0 &&
+						col >= data.Length % cols)
+						Console.Write ("   ");
+					else
+						Console.Write (" {0:x2}",
+							data [row * cols + col]);
+				}
+				
+				Console.Write (" | ");
+				
+				for (int col = 0; col < cols; col ++) {
+					if (row == rows - 1 &&
+						data.Length % cols != 0 &&
+						col >= data.Length % cols)
+						Console.Write (" ");
+					else
+						WriteByte2 (
+							data [row * cols + col]);
+				}
+				
+				Console.WriteLine ();
+			}
+			Console.WriteLine ();
+		}
+
+		private static void WriteByte2 (byte data)
+		{
+			foreach (char c in allowed)
+				if (c == data) {
+					Console.Write (c);
+					return;
+				}
+			
+			Console.Write (".");
+		}
+		
+		private static string allowed = "0123456789abcdefghijklmnopqr" +
+			"stuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~! #$%^&*()_+-={}" +
+			"[];:'\",.<>?/\\|";
+
+
+		private static Dictionary <object, Dictionary <object, DebugTimeData>>
+			debug_times = new Dictionary <object, Dictionary <object, DebugTimeData>> ();
+
+		public static void AddDebugTime (object o1, object o2, DateTime start)
+		{
+			DebugTimeData data = new DebugTimeData (DateTime.Now - start, 1);
+			if (debug_times.ContainsKey (o1) && debug_times [o1].ContainsKey (o2)) {
+				data.time       += debug_times [o1][o2].time;
+				data.occurances += debug_times [o1][o2].occurances;
+			}
+
+			if (!debug_times.ContainsKey (o1))
+				debug_times.Add (o1, new Dictionary <object, DebugTimeData> ());
+
+			if (!debug_times [o1].ContainsKey (o2))
+				debug_times [o1].Add (o2, data);
+			else
+				debug_times [o1][o2] = data;
+		}
+
+		public static void DumpDebugTime (object o1)
+		{
+			Console.WriteLine (o1.ToString ());
+			if (!debug_times.ContainsKey (o1))
+				return;
+
+			foreach (KeyValuePair <object, DebugTimeData> pair in debug_times [o1]) {
+				Console.WriteLine ("  {0}", pair.Key.ToString ());
+				Console.WriteLine ("    Objects: {0}", pair.Value.time);
+				Console.WriteLine ("    Total:   {0}", pair.Value.occurances);
+				Console.WriteLine ("    Average: {0}", new TimeSpan (pair.Value.time.Ticks / pair.Value.occurances));
+				Console.WriteLine (String.Empty);
+			}
+			
+			debug_times.Remove (o1);
+		}
+
+		private struct DebugTimeData
+		{
+			public TimeSpan time;
+			public long occurances;
+
+			public DebugTimeData (TimeSpan time, int occurances)
+			{
+				this.time = time;
+				this.occurances = occurances;
+			}
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/File.cs b/lib/TagLib/TagLib/File.cs
new file mode 100644
index 0000000..bb175bc
--- /dev/null
+++ b/lib/TagLib/TagLib/File.cs
@@ -0,0 +1,1774 @@
+//
+// File.cs: Provides a basic framework for reading from and writing to
+// a file, as well as accessing basic tagging and media properties.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//   Aaron Bockover (abockover novell com)
+//
+// Original Source:
+//   tfile.cpp from TagLib
+//
+// Copyright (C) 2005, 2007 Brian Nickel
+// Copyright (C) 2006 Novell, Inc.
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace TagLib {
+	
+	/// <summary>
+	///    Specifies the level of intensity to use when reading the media
+	///    properties.
+	/// </summary>
+	public enum ReadStyle {
+		/// <summary>
+		///    The media properties will not be read.
+		/// </summary>
+		None = 0,
+		
+		// Fast = 1,
+		
+		/// <summary>
+		///    The media properties will be read with average accuracy.
+		/// </summary>
+		Average = 2,
+		
+		// Accurate = 3
+	}
+	
+	/// <summary>
+	///    This abstract class provides a basic framework for reading from
+	///    and writing to a file, as well as accessing basic tagging and
+	///    media properties.
+	/// </summary>
+	/// <remarks>
+	///    <para>This class is agnostic to all specific media types. Its
+	///    child classes, on the other hand, support the the intricacies of
+	///    different media and tagging formats. For example, <see
+	///    cref="Mpeg4.File" /> supports the MPEG-4 specificication and
+	///    Apple's tagging format.</para>
+	///    <para>Each file type can be created using its format specific
+	///    constructors, ie. <see cref="Mpeg4.File(string)" />, but the
+	///    preferred method is to use <see
+	///    cref="File.Create(string,string,ReadStyle)" /> or one of its
+	///    variants, as it automatically detects the appropriate class from
+	///    the file extension or provided mime-type.</para>
+	/// </remarks>
+	public abstract class File : IDisposable
+	{
+		#region Enums
+		
+		/// <summary>
+		///   Specifies the type of file access operations currently
+		///   permitted on an instance of <see cref="File" />.
+		/// </summary>
+		public enum AccessMode {
+			/// <summary>
+			///    Read operations can be performed.
+			/// </summary>
+			Read,
+		
+			/// <summary>
+			///    Read and write operations can be performed.
+			/// </summary>
+			Write,
+		
+			/// <summary>
+			///    The file is closed for both read and write
+			///    operations.
+			/// </summary>
+			Closed
+		}
+		
+		#endregion
+		
+		
+		
+		#region Delegates
+		
+		/// <summary>
+		///    This delegate is used for intervening in <see
+		///    cref="File.Create(string)" /> by resolving the file type
+		///    before any standard resolution operations.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object representing the
+		///    file to be read.
+		/// </param>
+		/// <param name="mimetype">
+		///    A <see cref="string" /> object containing the mime-type
+		///    of the file.
+		/// </param>
+		/// <param name="style">
+		///    A <see cref="ReadStyle" /> value specifying how to read
+		///    media properties from the file.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="File" /> or <see
+		///    langword="null" /> if the resolver could not match it.
+		/// </returns>
+		/// <remarks>
+		///    <para>A <see cref="FileTypeResolver" /> is one way of
+		///    altering the behavior of <see cref="File.Create(string)" />
+		///    .</para>
+		///    <para>When <see cref="File.Create(string)" /> is called, the
+		///    registered resolvers are invoked in the reverse order in
+		///    which they were registered. The resolver may then perform
+		///    any operations necessary, including other type-finding
+		///    methods.</para>
+		///    <para>If the resolver returns a new <see cref="File" />,
+		///    it will instantly be returned, by <see
+		///    cref="File.Create(string)" />. If it returns <see 
+		///    langword="null" />, <see cref="File.Create(string)" /> will
+		///    continue to process. If the resolver throws an exception
+		///    it will be uncaught.</para>
+		///    <para>To register a resolver, use <see
+		///    cref="AddFileTypeResolver" />.</para>
+		/// </remarks>
+		public delegate File FileTypeResolver (IFileAbstraction abstraction,
+		                                       string mimetype,
+		                                       ReadStyle style);
+		
+		#endregion
+		
+		
+		
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the current stream used in reading/writing.
+		/// </summary>
+		private System.IO.Stream file_stream;
+		
+		/// <summary>
+		///    Contains the internal file abstraction.
+		/// </summary>
+		private IFileAbstraction file_abstraction;
+		
+		/// <summary>
+		///    Contains the mime-type of the file as provided by <see
+		///    cref="Create(string)" />.
+		/// </summary>
+		private string mime_type;
+		
+		/// <summary>
+		///    Contains the types of tags in the file on disk.
+		/// </summary>
+		private TagTypes tags_on_disk = TagTypes.None;
+		
+		/// <summary>
+		///    Contains buffer size to use when reading.
+		/// </summary>
+		private static int buffer_size = 1024;
+		
+		/// <summary>
+		///    Contains the file type resolvers to use in <see
+		///    cref="Create(string)" />.
+		/// </summary>
+		private static List<FileTypeResolver> file_type_resolvers
+			= new List<FileTypeResolver> ();
+		
+		/// <summary>
+		///    Contains position at which the invariant data portion of
+		///    the file begins.
+		/// </summary>
+		private long invariant_start_position = -1;
+		
+		/// <summary>
+		///    Contains position at which the invariant data portion of
+		///    the file ends.
+		/// </summary>
+		private long invariant_end_position = -1;
+		#endregion
+		
+		
+		
+		#region Public Static Properties
+		
+		/// <summary>
+		///    The buffer size to use when reading large blocks of data
+		///    in the <see cref="File" /> class.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the buffer size to use
+		///    when reading large blocks of data.
+		/// </value>
+		public static uint BufferSize {
+			get {return (uint) buffer_size;}
+		}
+		
+		#endregion
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		protected File (string path)
+		{
+			if (path == null)
+				throw new ArgumentNullException ("path");
+			
+			file_abstraction = new LocalFileAbstraction (path);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		protected File (IFileAbstraction abstraction)
+		{
+			if (abstraction == null)
+				throw new ArgumentNullException ("abstraction");
+			
+			file_abstraction = abstraction;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    <para>This property provides generic and general access
+		///    to the most common tagging features of a file. To access
+		///    or add a specific type of tag in the file, use <see
+		///    cref="GetTag(TagLib.TagTypes,bool)" />.</para>
+		/// </remarks>
+		public abstract Tag Tag {get;}
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public abstract Properties Properties {get;}
+		
+		/// <summary>
+		///    Gets the tag types contained in the physical file
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing the tag types stored in the physical file as
+		///    it was read or last saved.
+		/// </value>
+		public TagTypes TagTypesOnDisk {
+			get {return tags_on_disk;}
+			protected set {tags_on_disk = value;}
+		}
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing the tag types stored in the current instance.
+		/// </value>
+		public TagTypes TagTypes {
+			get {return Tag != null ? Tag.TagTypes : TagTypes.None;}
+		}
+		
+		/// <summary>
+		///    Gets the name of the file as stored in its file
+		///    abstraction.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the name of the
+		///    file as stored in the <see cref="IFileAbstraction" />
+		///    object used to create it or the path if created with a
+		///    local path.
+		/// </value>
+		public string Name {
+			get {return file_abstraction.Name;}
+		}
+		
+		/// <summary>
+		///    Gets the mime-type of the file as determined by <see
+		///    cref="Create(IFileAbstraction,string,ReadStyle)" /> if
+		///    that method was used to create the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the mime-type
+		///    used to create the file or <see langword="null" /> if <see
+		///    cref="Create(IFileAbstraction,string,ReadStyle)" /> was
+		///    not used to create the current instance.
+		/// </value>
+		public string MimeType {
+			get {return mime_type;}
+			internal set {mime_type = value;}
+		}
+		
+		/// <summary>
+		///    Gets the seek position in the internal stream used by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value representing the seek
+		///    position, or 0 if the file is not open for reading.
+		/// </value>
+		public long Tell {
+			get {return (Mode == AccessMode.Closed) ?
+				0 : file_stream.Position;}
+		}
+		
+		/// <summary>
+		///    Gets the length of the file represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value representing the size of the
+		///    file, or 0 if the file is not open for reading.
+		/// </value>
+		public long Length {
+			get {return (Mode == AccessMode.Closed) ?
+				0 : file_stream.Length;}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the invariant portion of the
+		///    current instance begins.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value representing the seek
+		///    position at which the file's invariant (media) data
+		///    section begins. If the value could not be determined,
+		///    <c>-1</c> is returned.
+		/// </value>
+		public long InvariantStartPosition {
+			get {return invariant_start_position;}
+			protected set {invariant_start_position = value;}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the invariant portion of the
+		///    current instance ends.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value representing the seek
+		///    position at which the file's invariant (media) data
+		///    section ends. If the value could not be determined,
+		///    <c>-1</c> is returned.
+		/// </value>
+		public long InvariantEndPosition {
+			get {return invariant_end_position;}
+			protected set {invariant_end_position = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the file access mode in use by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="AccessMode" /> value describing the features
+		///    of stream currently in use by the current instance.
+		/// </value>
+		/// <remarks>
+		///    Changing the value will cause the stream currently in use
+		///    to be closed, except when a change is made from <see
+		///    cref="AccessMode.Write" /> to <see cref="AccessMode.Read"
+		///    /> which has no effect.
+		/// </remarks>
+		public AccessMode Mode {
+			get {return (file_stream == null) ?
+				AccessMode.Closed : (file_stream.CanWrite) ?
+					AccessMode.Write : AccessMode.Read;}
+			set {
+				if (Mode == value || (Mode == AccessMode.Write
+					&& value == AccessMode.Read))
+					return;
+				
+				if (file_stream != null)
+					file_abstraction.CloseStream (file_stream);
+				
+				file_stream = null;
+				
+				if (value == AccessMode.Read)
+					file_stream = file_abstraction.ReadStream;
+				else if (value == AccessMode.Write)
+					file_stream = file_abstraction.WriteStream;
+				
+				Mode = value;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+
+		/// <summary>
+		///    Dispose the current file. Equivalent to setting the
+		///    mode to closed
+		/// </summary>
+		public void Dispose ()
+		{
+			Mode = AccessMode.Closed;
+		}
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public abstract void Save ();
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public abstract void RemoveTags (TagTypes types);
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    <para>Passing <see langword="true" /> to <paramref
+		///    name="create" /> does not guarantee the tag will be
+		///    created. For example, trying to create an ID3v2 tag on an
+		///    OGG Vorbis file will always fail.</para>
+		///    <para>It is safe to assume that if <see langword="null"
+		///    /> is not returned, the returned tag can be cast to the
+		///    appropriate type.</para>
+		/// </remarks>
+		/// <example>
+		///    <para>The following example sets the mood of a file to
+		///    several tag types.</para>
+		///    <code lang="C#">string [] SetMoods (TagLib.File file, params string[] moods)
+		///{
+		///   TagLib.Id3v2.Tag id3 = file.GetTag (TagLib.TagTypes.Id3v2, true);
+		///   if (id3 != null)
+		///      id3.SetTextFrame ("TMOO", moods);
+		///   
+		///   TagLib.Asf.Tag asf = file.GetTag (TagLib.TagTypes.Asf, true);
+		///   if (asf != null)
+		///      asf.SetDescriptorStrings (moods, "WM/Mood", "Mood");
+		///   
+		///   TagLib.Ape.Tag ape = file.GetTag (TagLib.TagTypes.Ape);
+		///   if (ape != null)
+		///      ape.SetValue ("MOOD", moods);
+		///      
+		///   // Whatever tag types you want...
+		///}</code>
+		/// </example>
+		public abstract Tag GetTag (TagTypes type, bool create);
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in the current instance. If no matching tag
+		///    was found, <see langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    <para>This class merely accesses the tag if it exists.
+		///    <see cref="GetTag(TagTypes,bool)" /> provides the option
+		///    of adding the tag to the current instance if it does not
+		///    exist.</para>
+		///    <para>It is safe to assume that if <see langword="null"
+		///    /> is not returned, the returned tag can be cast to the
+		///    appropriate type.</para>
+		/// </remarks>
+		/// <example>
+		///    <para>The following example reads the mood of a file from
+		///    several tag types.</para>
+		///    <code lang="C#">static string [] GetMoods (TagLib.File file)
+		///{
+		///   TagLib.Id3v2.Tag id3 = file.GetTag (TagLib.TagTypes.Id3v2);
+		///   if (id3 != null) {
+		///      TextIdentificationFrame f = TextIdentificationFrame.Get (this, "TMOO");
+		///      if (f != null)
+		///         return f.FieldList.ToArray ();
+		///   }
+		///   
+		///   TagLib.Asf.Tag asf = file.GetTag (TagLib.TagTypes.Asf);
+		///   if (asf != null) {
+		///      string [] value = asf.GetDescriptorStrings ("WM/Mood", "Mood");
+		///      if (value.Length &gt; 0)
+		///         return value;
+		///   }
+		///   
+		///   TagLib.Ape.Tag ape = file.GetTag (TagLib.TagTypes.Ape);
+		///   if (ape != null) {
+		///      Item item = ape.GetItem ("MOOD");
+		///      if (item != null)
+		///         return item.ToStringArray ();
+		///   }
+		///      
+		///   // Whatever tag types you want...
+		///   
+		///   return new string [] {};
+		///}</code>
+		/// </example>
+		public Tag GetTag (TagTypes type)
+		{
+			return GetTag (type, false);
+		}
+		
+		/// <summary>
+		///    Reads a specified number of bytes at the current seek
+		///    position from the current instance.
+		/// </summary>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to read.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    read from the current instance.
+		/// </returns>
+		/// <remarks>
+		///    <para>This method reads the block of data at the current
+		///    seek position. To change the seek position, use <see
+		///    cref="Seek(long,System.IO.SeekOrigin)" />.</para>
+		/// </remarks>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="length" /> is less than zero.
+		/// </exception>
+		public ByteVector ReadBlock (int length)
+		{
+			if (length < 0)
+				throw new ArgumentException (
+					"Length must be non-negative",
+					"length");
+			
+			if (length == 0)
+				return new ByteVector ();
+			
+			Mode = AccessMode.Read;
+			
+			byte [] buffer = new byte [length];
+			int count = file_stream.Read (buffer, 0, length);
+			return new ByteVector (buffer, count);
+		}
+		
+		/// <summary>
+		///    Writes a block of data to the file represented by the
+		///    current instance at the current seek position.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing data to be
+		///    written to the current instance.
+		/// </param>
+		/// <remarks>
+		///    This will overwrite any existing data at the seek
+		///    position and append new data to the file if writing past
+		///    the current end.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public void WriteBlock (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			Mode = AccessMode.Write;
+			
+			file_stream.Write (data.Data, 0, data.Count);
+		}
+		
+		/// <summary>
+		///    Searches forwards through a file for a specified
+		///    pattern, starting at a specified offset.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector" /> object containing a pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="startPosition">
+		///    A <see cref="int" /> value specifying at what
+		///    seek position to start searching.
+		/// </param>
+		/// <param name="before">
+		///    A <see cref="ByteVector" /> object specifying a pattern
+		///    that the searched for pattern must appear before. If this
+		///    pattern is found first, -1 is returned.
+		/// </param>
+		/// <returns>
+		///    A <see cref="long" /> value containing the index at which
+		///    the value was found. If not found, -1 is returned.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public long Find (ByteVector pattern, long startPosition,
+		                  ByteVector before)
+		{
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			Mode = AccessMode.Read;
+			
+			if (pattern.Count > buffer_size)
+				return -1;
+			
+			// The position in the file that the current buffer
+			// starts at.
+			
+			long buffer_offset = startPosition;
+			ByteVector buffer;
+			
+			// These variables are used to keep track of a partial
+			// match that happens at the end of a buffer.
+			
+			int previous_partial_match = -1;
+			int before_previous_partial_match = -1;
+			
+			// Save the location of the current read pointer.  We
+			// will restore the position using Seek() before all
+			// returns.
+			
+			long original_position = file_stream.Position;
+			
+			// Start the search at the offset.
+			
+			file_stream.Position = startPosition;
+			
+			// 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 iteration, 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 (buffer_size); 
+				buffer.Count > 0;
+				buffer = ReadBlock(buffer_size)) {
+				
+				// (1) previous partial match
+				
+				if (previous_partial_match >= 0 &&
+					buffer_size > previous_partial_match) {
+					int pattern_offset = buffer_size -
+						previous_partial_match;
+					
+					if (buffer.ContainsAt (pattern, 0,
+						pattern_offset)) {
+						
+						file_stream.Position =
+							original_position;
+						
+						return buffer_offset -
+							buffer_size +
+							previous_partial_match;
+					}
+				}
+				
+				if (before != null &&
+					before_previous_partial_match >= 0 &&
+					buffer_size >
+					before_previous_partial_match) {
+					
+					int before_offset = buffer_size -
+						before_previous_partial_match;
+					
+					if (buffer.ContainsAt (before, 0,
+						before_offset)) {
+						
+						file_stream.Position =
+							original_position;
+						
+						return -1;
+					}
+				}
+				
+				// (2) pattern contained in current buffer
+				
+				long location = buffer.Find (pattern);
+				
+				if (location >= 0) {
+					file_stream.Position = original_position;
+					return buffer_offset + location;
+				}
+				
+				if (before != null && buffer.Find (before) >= 0) {
+					file_stream.Position = original_position;
+					return -1;
+				}
+				
+				// (3) partial match
+				
+				previous_partial_match =
+					buffer.EndsWithPartialMatch (pattern);
+				
+				if (before != null)
+					before_previous_partial_match =
+						buffer.EndsWithPartialMatch (
+							before);
+				
+				buffer_offset += buffer_size;
+			}
+			
+			// Since we hit the end of the file, reset the status
+			// before continuing.
+			
+			file_stream.Position = original_position;
+			return -1;
+		}
+		
+		/// <summary>
+		///    Searches forwards through a file for a specified
+		///    pattern, starting at a specified offset.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector" /> object containing a pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="startPosition">
+		///    A <see cref="int" /> value specifying at what
+		///    seek position to start searching.
+		/// </param>
+		/// <returns>
+		///    A <see cref="long" /> value containing the index at which
+		///    the value was found. If not found, -1 is returned.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public long Find (ByteVector pattern, long startPosition)
+		{
+			return Find (pattern, startPosition, null);
+		}
+		
+		/// <summary>
+		///    Searches forwards through a file for a specified
+		///    pattern, starting at the beginning of the file.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector" /> object containing a pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="long" /> value containing the index at which
+		///    the value was found. If not found, -1 is returned.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public long Find (ByteVector pattern)
+		{
+			return Find (pattern, 0);
+		}
+		
+		/// <summary>
+		///    Searches backwards through a file for a specified
+		///    pattern, starting at a specified offset.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector" /> object containing a pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="startPosition">
+		///    A <see cref="int" /> value specifying at what
+		///    seek position to start searching.
+		/// </param>
+		/// <param name="after">
+		///    A <see cref="ByteVector" /> object specifying a pattern
+		///    that the searched for pattern must appear after. If this
+		///    pattern is found first, -1 is returned.
+		/// </param>
+		/// <returns>
+		///    A <see cref="long" /> value containing the index at which
+		///    the value was found. If not found, -1 is returned.
+		/// </returns>
+		/// <remarks>
+		///    Searching for <paramref name="after" /> is not yet
+		///    implemented.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		long RFind (ByteVector pattern, long startPosition,
+		            ByteVector after)
+		{
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			Mode = AccessMode.Read;
+			
+			if (pattern.Count > buffer_size)
+				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 previous_partial_match = -1;
+			int after_previous_partial_match = -1;
+			*/
+			
+			// Save the location of the current read pointer.  We
+			// will restore the position using Seek() before all 
+			// returns.
+			
+			long original_position = file_stream.Position;
+			
+			// Start the search at the offset.
+			
+			long buffer_offset = Length - startPosition;
+			int read_size = buffer_size;
+			
+			read_size = (int) Math.Min (buffer_offset, buffer_size);
+			buffer_offset -= read_size;
+			file_stream.Position = buffer_offset;
+			
+			// See the notes in find() for an explanation of this
+			// algorithm.
+			
+			for (buffer = ReadBlock (read_size); buffer.Count > 0;
+				buffer = ReadBlock (read_size)) {
+				
+				// TODO: (1) previous partial match
+				
+				// (2) pattern contained in current buffer
+				
+				long location = buffer.RFind (pattern);
+				if (location >= 0) {
+					file_stream.Position = original_position;
+					return buffer_offset + location;
+				}
+				
+				if(after != null && buffer.RFind (after) >= 0) {
+					file_stream.Position = original_position;
+					return -1;
+				}
+				
+				// TODO: (3) partial match
+
+				
+				read_size = (int) Math.Min (buffer_offset, buffer_size);
+				buffer_offset -= read_size;
+			
+				file_stream.Position = buffer_offset;
+			}
+			
+			// Since we hit the end of the file, reset the status
+			// before continuing.
+			
+			file_stream.Position = original_position;
+			return -1;
+		}
+		
+		/// <summary>
+		///    Searches backwards through a file for a specified
+		///    pattern, starting at a specified offset.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector" /> object containing a pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <param name="startPosition">
+		///    A <see cref="int" /> value specifying at what
+		///    seek position to start searching.
+		/// </param>
+		/// <returns>
+		///    A <see cref="long" /> value containing the index at which
+		///    the value was found. If not found, -1 is returned.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public long RFind (ByteVector pattern, long startPosition)
+		{
+			return RFind (pattern, startPosition, null);
+		}
+		
+		/// <summary>
+		///    Searches backwards through a file for a specified
+		///    pattern, starting at the end of the file.
+		/// </summary>
+		/// <param name="pattern">
+		///    A <see cref="ByteVector" /> object containing a pattern
+		///    to search for in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="long" /> value containing the index at which
+		///    the value was found. If not found, -1 is returned.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="pattern" /> is <see langword="null" />.
+		/// </exception>
+		public long RFind (ByteVector pattern)
+		{
+			return RFind (pattern, 0);
+		}
+		
+		/// <summary>
+		///    Inserts a specifed block of data into the file repesented
+		///    by the current instance at a specified location,
+		///    replacing a specified number of bytes.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the data to
+		///    insert into the file.
+		/// </param>
+		/// <param name="start">
+		///    A <see cref="long" /> value specifying at which point to
+		///    insert the data.
+		/// </param>
+		/// <param name="replace">
+		///    A <see cref="long" /> value specifying the number of
+		///    bytes to replace. Typically this is the original size of
+		///    the data block so that a new block will replace the old
+		///    one.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public void Insert (ByteVector data, long start, long replace)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			Mode = AccessMode.Write;
+			
+			if (data.Count == replace) {
+				file_stream.Position = start;
+				WriteBlock (data);
+				return;
+			} else if(data.Count < replace) {
+				file_stream.Position = start;
+				WriteBlock (data);
+				RemoveBlock (start + data.Count,
+					replace - data.Count);
+				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.
+			
+			int buffer_length = buffer_size;
+			
+			while (data.Count - replace > buffer_length)
+				buffer_length += (int) BufferSize;
+			
+			// Set where to start the reading and writing.
+			
+			long read_position = start + replace;
+			long write_position = start;
+			
+			byte [] buffer;
+			byte [] about_to_overwrite;
+			
+			// 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.
+			
+			file_stream.Position = read_position;
+			about_to_overwrite = ReadBlock (buffer_length).Data;
+			read_position += buffer_length;
+			
+			file_stream.Position = write_position;
+			WriteBlock (data);
+			write_position += data.Count;
+			
+			buffer = new byte [about_to_overwrite.Length];
+			System.Array.Copy (about_to_overwrite, 0, buffer, 0,
+				about_to_overwrite.Length);
+			
+			// 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_length != 0) {
+				// Seek to the current read position and read
+				// the data that we're about to overwrite. 
+				// Appropriately increment the readPosition.
+				
+				file_stream.Position = read_position;
+				int bytes_read = file_stream.Read (
+					about_to_overwrite, 0, buffer_length <
+					about_to_overwrite.Length ?
+						buffer_length :
+						about_to_overwrite.Length);
+				read_position += buffer_length;
+				
+				// Seek to the write position and write our
+				// buffer. Increment the writePosition.
+				
+				file_stream.Position = write_position;
+				file_stream.Write (buffer, 0,
+					buffer_length < buffer.Length ?
+						buffer_length : buffer.Length);
+				write_position += buffer_length;
+				
+				// Make the current buffer the data that we read
+				// in the beginning.
+				
+				System.Array.Copy (about_to_overwrite, 0,
+					buffer, 0, bytes_read);
+				
+				// 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.
+				
+				buffer_length = bytes_read;
+			}
+		}
+		
+		/// <summary>
+		///    Inserts a specifed block of data into the file repesented
+		///    by the current instance at a specified location.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the data to
+		///    insert into the file.
+		/// </param>
+		/// <param name="start">
+		///    A <see cref="long" /> value specifying at which point to
+		///    insert the data.
+		/// </param>
+		/// <remarks>
+		///    This method inserts a new block of data into the file. To
+		///    replace an existing block, ie. replacing an existing
+		///    tag with a new one of different size, use <see
+		///    cref="Insert(ByteVector,long,long)" />.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public void Insert (ByteVector data, long start)
+		{
+			Insert (data, start, 0);
+		}
+		
+		/// <summary>
+		///    Removes a specified block of data from the file
+		///    represented by the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value specifying at which point to
+		///    remove data.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="long" /> value specifying the number of
+		///    bytes to remove.
+		/// </param>
+		public void RemoveBlock (long start, long length)
+		{
+			if (length <= 0)
+				return;
+			
+			Mode = AccessMode.Write;
+			
+			int buffer_length = buffer_size;
+			
+			long read_position = start + length;
+			long write_position = start;
+			
+			ByteVector buffer = (byte) 1;
+			
+			while(buffer.Count != 0) {
+				file_stream.Position = read_position;
+				buffer = ReadBlock (buffer_length);
+				read_position += buffer.Count;
+				
+				file_stream.Position = write_position;
+				WriteBlock (buffer);
+				write_position += buffer.Count;
+			}
+			
+			Truncate (write_position);
+		}
+		
+		/// <summary>
+		///    Seeks the read/write pointer to a specified offset in the
+		///    current instance, relative to a specified origin.
+		/// </summary>
+		/// <param name="offset">
+		///    A <see cref="long" /> value indicating the byte offset to
+		///    seek to.
+		/// </param>
+		/// <param name="origin">
+		///    A <see cref="System.IO.SeekOrigin" /> value specifying an
+		///    origin to seek from.
+		/// </param>
+		public void Seek (long offset, System.IO.SeekOrigin origin)
+		{
+			if (Mode != AccessMode.Closed)
+				file_stream.Seek (offset, origin);
+		}
+		
+		/// <summary>
+		///    Seeks the read/write pointer to a specified offset in the
+		///    current instance, relative to the beginning of the file.
+		/// </summary>
+		/// <param name="offset">
+		///    A <see cref="long" /> value indicating the byte offset to
+		///    seek to.
+		/// </param>
+		public void Seek (long offset)
+		{
+			Seek (offset, System.IO.SeekOrigin.Begin);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Creates a new instance of a <see cref="File" /> subclass
+		///    for a specified path, guessing the mime-type from the
+		///    file's extension and using the average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object specifying the file to
+		///    read from and write to.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="File" /> as read from the
+		///    specified path.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    The file could not be read due to corruption.
+		/// </exception>
+		/// <exception cref="UnsupportedFormatException">
+		///    The file could not be read because the mime-type could
+		///    not be resolved or the library does not support an
+		///    internal feature of the file crucial to its reading.
+		/// </exception>
+		public static File Create (string path)
+		{
+			return Create(path, null, ReadStyle.Average);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of a <see cref="File" /> subclass
+		///    for a specified file abstraction, guessing the mime-type
+		///    from the file's extension and using the average read
+		///    style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading to and writing from the current instance.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="File" /> as read from the
+		///    specified abstraction.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    The file could not be read due to corruption.
+		/// </exception>
+		/// <exception cref="UnsupportedFormatException">
+		///    The file could not be read because the mime-type could
+		///    not be resolved or the library does not support an
+		///    internal feature of the file crucial to its reading.
+		/// </exception>
+		public static File Create (IFileAbstraction abstraction)
+		{
+			return Create(abstraction, null, ReadStyle.Average);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of a <see cref="File" /> subclass
+		///    for a specified path and read style, guessing the
+		///    mime-type from the file's extension.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object specifying the file to
+		///    read from and write to.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying the level of
+		///    detail to use when reading the media information from the
+		///    new instance.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="File" /> as read from the
+		///    specified path.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    The file could not be read due to corruption.
+		/// </exception>
+		/// <exception cref="UnsupportedFormatException">
+		///    The file could not be read because the mime-type could
+		///    not be resolved or the library does not support an
+		///    internal feature of the file crucial to its reading.
+		/// </exception>
+		public static File Create (string path,
+		                           ReadStyle propertiesStyle)
+		{
+			return Create(path, null, propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of a <see cref="File" /> subclass
+		///    for a specified file abstraction and read style, guessing
+		///    the mime-type from the file's extension.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading to and writing from the current instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying the level of
+		///    detail to use when reading the media information from the
+		///    new instance.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="File" /> as read from the
+		///    specified abstraction.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    The file could not be read due to corruption.
+		/// </exception>
+		/// <exception cref="UnsupportedFormatException">
+		///    The file could not be read because the mime-type could
+		///    not be resolved or the library does not support an
+		///    internal feature of the file crucial to its reading.
+		/// </exception>
+		public static File Create (IFileAbstraction abstraction,
+		                           ReadStyle propertiesStyle)
+		{
+			return Create(abstraction, null, propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of a <see cref="File" /> subclass
+		///    for a specified path, mime-type, and read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object specifying the file to
+		///    read from and write to.
+		/// </param>
+		/// <param name="mimetype">
+		///    A <see cref="string" /> object containing the mime-type
+		///    to use when selecting the appropriate class to use, or
+		///    <see langword="null" /> if the extension in <paramref
+		///    name="abstraction" /> is to be used.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying the level of
+		///    detail to use when reading the media information from the
+		///    new instance.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="File" /> as read from the
+		///    specified path.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    The file could not be read due to corruption.
+		/// </exception>
+		/// <exception cref="UnsupportedFormatException">
+		///    The file could not be read because the mime-type could
+		///    not be resolved or the library does not support an
+		///    internal feature of the file crucial to its reading.
+		/// </exception>
+		public static File Create (string path, string mimetype,
+		                           ReadStyle propertiesStyle)
+		{
+			return Create (new LocalFileAbstraction (path),
+				mimetype, propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Creates a new instance of a <see cref="File" /> subclass
+		///    for a specified file abstraction, mime-type, and read
+		///    style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading to and writing from the current instance.
+		/// </param>
+		/// <param name="mimetype">
+		///    A <see cref="string" /> object containing the mime-type
+		///    to use when selecting the appropriate class to use, or
+		///    <see langword="null" /> if the extension in <paramref
+		///    name="abstraction" /> is to be used.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying the level of
+		///    detail to use when reading the media information from the
+		///    new instance.
+		/// </param>
+		/// <returns>
+		///    A new instance of <see cref="File" /> as read from the
+		///    specified abstraction.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    The file could not be read due to corruption.
+		/// </exception>
+		/// <exception cref="UnsupportedFormatException">
+		///    The file could not be read because the mime-type could
+		///    not be resolved or the library does not support an
+		///    internal feature of the file crucial to its reading.
+		/// </exception>
+		public static File Create (IFileAbstraction abstraction,
+		                           string mimetype,
+		                           ReadStyle propertiesStyle)
+		{
+			if(mimetype == null) {
+				string ext = String.Empty;
+				
+				int index = abstraction.Name.LastIndexOf (".") + 1;
+				
+				if(index >= 1 && index < abstraction.Name.Length)
+					ext = abstraction.Name.Substring (index,
+						abstraction.Name.Length - index);
+				
+				mimetype = "taglib/" + ext.ToLower(
+					CultureInfo.InvariantCulture);
+			}
+			
+			foreach (FileTypeResolver resolver in file_type_resolvers) {
+				File file = resolver(abstraction, mimetype,
+					propertiesStyle);
+				
+				if(file != null)
+					return file;
+			}
+			
+			if (!FileTypes.AvailableTypes.ContainsKey(mimetype))
+				throw new UnsupportedFormatException (
+					String.Format (
+						CultureInfo.InvariantCulture,
+						"{0} ({1})",
+						abstraction.Name,
+						mimetype));
+			
+			Type file_type = FileTypes.AvailableTypes[mimetype];
+			
+			try {
+				File file = (File) Activator.CreateInstance(
+					file_type,
+					new object [] {abstraction, propertiesStyle});
+				
+				file.MimeType = mimetype;
+				return file;
+			} catch (System.Reflection.TargetInvocationException e) {
+				throw e.InnerException;
+			}
+		}
+		
+		/// <summary>
+		///    Adds a <see cref="FileTypeResolver" /> to the <see
+		///    cref="File" /> class. The one added last gets run first.
+		/// </summary>
+		/// <param name="resolver">
+		///    A <see cref="FileTypeResolver" /> delegate to add to the
+		///    file type recognition stack.
+		/// </param>
+		/// <remarks>
+		///    A <see cref="FileTypeResolver" /> adds support for 
+		///    recognizing a file type outside of the standard mime-type
+		///    methods.
+		/// </remarks>
+		public static void AddFileTypeResolver (FileTypeResolver resolver)
+		{
+			if (resolver != null)
+				file_type_resolvers.Insert (0, resolver);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Resized the current instance to a specified number of
+		///    bytes.
+		/// </summary>
+		/// <param name="length">
+		///    A <see cref="long" /> value specifying the number of
+		///    bytes to resize the file to.
+		/// </param>
+		protected void Truncate (long length)
+		{
+			AccessMode old_mode = Mode;
+			Mode = AccessMode.Write;
+			file_stream.SetLength (length);
+			Mode = old_mode;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Classes
+		
+		/// <summary>
+		///    This class implements <see cref="IFileAbstraction" />
+		///    to provide support for accessing the local/standard file
+		///    system.
+		/// </summary>
+		/// <remarks>
+		///    This class is used as the standard file abstraction
+		///    throughout the library.
+		/// </remarks>
+		public class LocalFileAbstraction : IFileAbstraction
+		{
+			/// <summary>
+			///    Contains the name used to open the file.
+			/// </summary>
+			private string name;
+			
+			/// <summary>
+			///    Constructs and initializes a new instance of
+			///    <see cref="LocalFileAbstraction" /> for a
+			///    specified path in the local file system.
+			/// </summary>
+			/// <param name="path">
+			///    A <see cref="string" /> object containing the
+			///    path of the file to use in the new instance.
+			/// </param>
+			/// <exception cref="ArgumentNullException">
+			///    <paramref name="path" /> is <see langword="null"
+			///    />.
+			/// </exception>
+			public LocalFileAbstraction (string path)
+			{
+				if (path == null)
+					throw new ArgumentNullException ("path");
+				
+				name = path;
+			}
+			
+			/// <summary>
+			///    Gets the path of the file represented by the
+			///    current instance.
+			/// </summary>
+			/// <value>
+			///    A <see cref="string" /> object containing the
+			///    path of the file represented by the current
+			///    instance.
+			/// </value>
+			public string Name {
+				get {return name;}
+			}
+			
+			/// <summary>
+			///    Gets a new readable, seekable stream from the
+			///    file represented by the current instance.
+			/// </summary>
+			/// <value>
+			///    A new <see cref="System.IO.Stream" /> to be used
+			///    when reading the file represented by the current
+			///    instance.
+			/// </value>
+			public System.IO.Stream ReadStream {
+				get {return System.IO.File.Open (Name,
+					System.IO.FileMode.Open,
+					System.IO.FileAccess.Read,
+					System.IO.FileShare.Read);}
+			}
+			
+			/// <summary>
+			///    Gets a new writable, seekable stream from the
+			///    file represented by the current instance.
+			/// </summary>
+			/// <value>
+			///    A new <see cref="System.IO.Stream" /> to be used
+			///    when writing to the file represented by the
+			///    current instance.
+			/// </value>
+			public System.IO.Stream WriteStream {
+				get {return System.IO.File.Open (Name,
+					System.IO.FileMode.Open,
+					System.IO.FileAccess.ReadWrite);}
+			}
+			
+			/// <summary>
+			///    Closes a stream created by the current instance.
+			/// </summary>
+			/// <param name="stream">
+			///    A <see cref="System.IO.Stream" /> object
+			///    created by the current instance.
+			/// </param>
+			public void CloseStream (System.IO.Stream stream)
+			{
+				if (stream == null)
+					throw new ArgumentNullException ("stream");
+				
+				stream.Close ();
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Interfaces
+		
+		/// <summary>
+		///    This interface provides abstracted access to a file. It
+		//     premits access to non-standard file systems and data
+		///    retrieval methods.
+		/// </summary>
+		/// <remarks>
+		///    <para>To use a custom abstraction, use <see
+		///    cref="Create(IFileAbstraction)" /> instead of <see
+		///    cref="Create(string)" /> when creating files.</para>
+		/// </remarks>
+		/// <example>
+		///    <para>The following example uses Gnome VFS to open a file
+		///    and read its title.</para>
+		/// <code lang="C#">using TagLib;
+		///using Gnome.Vfs;
+		///
+		///public class ReadTitle
+		///{
+		///   public static void Main (string [] args)
+		///   {
+		///      if (args.Length != 1)
+		///         return;
+		///
+		///      Gnome.Vfs.Vfs.Initialize ();
+		///      
+		///      try {
+		///          TagLib.File file = TagLib.File.Create (
+		///             new VfsFileAbstraction (args [0]));
+		///          System.Console.WriteLine (file.Tag.Title);
+		///      } finally {
+		///         Vfs.Shutdown()
+		///      }
+		///   }
+		///}
+		///
+		///public class VfsFileAbstraction : TagLib.File.IFileAbstraction
+		///{
+		///    private string name;
+		///
+		///    public VfsFileAbstraction (string file)
+		///    {
+		///        name = file;
+		///    }
+		///
+		///    public string Name {
+		///        get { return name; }
+		///    }
+		///
+		///    public System.IO.Stream ReadStream {
+		///        get { return new VfsStream(Name, System.IO.FileMode.Open); }
+		///    }
+		///
+		///    public System.IO.Stream WriteStream {
+		///        get { return new VfsStream(Name, System.IO.FileMode.Open); }
+		///    }
+		///
+		///    public void CloseStream (System.IO.Stream stream)
+		///    {
+		///        stream.Close ();
+		///    }
+		///}</code>
+		///    <code lang="Boo">import TagLib from "taglib-sharp.dll"
+		///import Gnome.Vfs from "gnome-vfs-sharp"
+		///
+		///class VfsFileAbstraction (TagLib.File.IFileAbstraction):
+		///        
+		///        _name as string
+		///        
+		///        def constructor(file as string):
+		///                _name = file
+		///        
+		///        Name:
+		///                get:
+		///                        return _name
+		///                
+		///        ReadStream:
+		///                get:
+		///                        return VfsStream(_name, FileMode.Open)
+		///                
+		///        WriteStream:
+		///                get:
+		///                        return VfsStream(_name, FileMode.Open)
+		///        
+		///if len(argv) == 1:
+		///        Vfs.Initialize()
+		///
+		///        try:
+		///                file as TagLib.File = TagLib.File.Create (VfsFileAbstraction (argv[0]))
+		///                print file.Tag.Title
+		///        ensure:
+		///                Vfs.Shutdown()</code>
+		/// </example>
+		public interface IFileAbstraction
+		{
+			/// <summary>
+			///    Gets the name or identifier used by the
+			///    implementation.
+			/// </summary>
+			/// <value>
+			///    A <see cref="string" /> object containing the 
+			///    name or identifier used by the implementation.
+			/// </value>
+			/// <remarks>
+			///    This value would typically represent a path or
+			///    URL to be used when identifying the file in the
+			///    file system, but it could be any value
+			///    as appropriate for the implementation.
+			/// </remarks>
+			string Name {get;}
+			
+			/// <summary>
+			///    Gets a readable, seekable stream for the file
+			///    referenced by the current instance.
+			/// </summary>
+			/// <value>
+			///    A <see cref="System.IO.Stream" /> object to be
+			///    used when reading a file.
+			/// </value>
+			/// <remarks>
+			///    This property is typically used when creating
+			///    constructing an instance of <see cref="File" />.
+			///    Upon completion of the constructor, <see
+			///    cref="CloseStream" /> will be called to close
+			///    the stream. If the stream is to be reused after
+			///    this point, <see cref="CloseStream" /> should be
+			///    implemented in a way to keep it open.
+			/// </remarks>
+			System.IO.Stream ReadStream  {get;}
+			
+			/// <summary>
+			///    Gets a writable, seekable stream for the file
+			///    referenced by the current instance.
+			/// </summary>
+			/// <value>
+			///    A <see cref="System.IO.Stream" /> object to be
+			///    used when writing to a file.
+			/// </value>
+			/// <remarks>
+			///    This property is typically used when saving a
+			///    file with <see cref="Save" />. Upon completion of
+			///    the method, <see cref="CloseStream" /> will be
+			///    called to close the stream. If the stream is to
+			///    be reused after this point, <see
+			///    cref="CloseStream" /> should be implemented in a
+			///    way to keep it open.
+			/// </remarks>
+			System.IO.Stream WriteStream {get;}
+			
+			/// <summary>
+			///    Closes a stream originating from the current
+			///    instance.
+			/// </summary>
+			/// <param name="stream">
+			///    A <see cref="System.IO.Stream" /> object
+			///    originating from the current instance.
+			/// </param>
+			/// <remarks>
+			///    If the stream is to be used outside of the scope,
+			///    of TagLib#, this method should perform no action.
+			///    For example, a stream that was created outside of
+			///    the current instance, or a stream that will
+			///    subsequently be used to play the file.
+			/// </remarks>
+			void CloseStream (System.IO.Stream stream);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/FileTypes.cs b/lib/TagLib/TagLib/FileTypes.cs
new file mode 100644
index 0000000..ff1c7f8
--- /dev/null
+++ b/lib/TagLib/TagLib/FileTypes.cs
@@ -0,0 +1,138 @@
+//
+// FileTypes.cs: Provides a mechanism for registering file classes and mime-
+// types, to be used when constructing a class via TagLib.File.Create.
+//
+// Author:
+//   Aaron Bockover (abockover novell com)
+//
+// Copyright (C) 2006 Novell, Inc.
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib {
+	/// <summary>
+	///    This static class provides a mechanism for registering file
+	///    classes and mime-types, to be used when constructing a class via
+	///    <see cref="File.Create(string)" />.
+	/// </summary>
+	/// <remarks>
+	///    <para>The default types built into the taglib-sharp.dll assembly
+	///    are registered automatically when the class is initialized. To
+	///    register your own custom types, use <see cref="Register"
+	///    />.</para>
+	/// </remarks>
+	/// <seealso cref="SupportedMimeType" />
+	public static class FileTypes
+	{
+		/// <summary>
+		///    Contains a mapping between mime-types and the <see
+		///    cref="File" /> subclasses that support them.
+		/// </summary>
+		private static Dictionary<string, Type> file_types;
+		
+		/// <summary>
+		///    Contains a static array of file types contained in the
+		///    TagLib# assembly.
+		/// </summary>
+		/// <remarks>
+		///    A static Type array is used instead of getting types by
+		///    reflecting the executing assembly as Assembly.GetTypes is
+		///    very inefficient and leaks every type instance under
+		///    Mono. Not reflecting taglib-sharp.dll saves about 120KB
+		///    of heap.
+		/// </remarks>
+		private static Type [] static_file_types = new Type [] {
+			typeof(TagLib.Aac.File),
+			typeof(TagLib.Aiff.File),
+			typeof(TagLib.Ape.File),
+			typeof(TagLib.Asf.File),
+			typeof(TagLib.Flac.File),
+			typeof(TagLib.MusePack.File),
+			typeof(TagLib.Mpeg4.File),
+			typeof(TagLib.Mpeg.File),
+			typeof(TagLib.Mpeg.AudioFile),
+			typeof(TagLib.Jpeg.File),
+			typeof(TagLib.Ogg.File),
+			typeof(TagLib.Riff.File),
+			typeof(TagLib.Tiff.File),
+			typeof(TagLib.WavPack.File)
+		};
+		
+		/// <summary>
+		///    Constructs and initializes the <see cref="FileTypes" />
+		///    class by registering the default types.
+		/// </summary>
+		static FileTypes ()
+		{
+			Init();
+		}
+		
+		/// <summary>
+		///    Initializes the class by registering the default types.
+		/// </summary>
+		internal static void Init ()
+		{
+			if(file_types != null)
+				return;
+			
+			file_types = new Dictionary<string, Type>();
+			
+			foreach(Type type in static_file_types)
+				Register (type);
+		}
+		
+		/// <summary>
+		///    Registers a <see cref="File" /> subclass to be used when
+		///    creating files via <see cref="File.Create(string)" />.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="Type" /> object for the class to register.
+		/// </param>
+		/// <remarks>
+		///    In order to register mime-types, the class represented by
+		///    <paramref name="type" /> should use the <see
+		///    cref="SupportedMimeType" /> custom attribute.
+		/// </remarks>
+		public static void Register (Type type)
+		{
+			Attribute [] attrs = Attribute.GetCustomAttributes (type,
+				typeof(SupportedMimeType));
+			
+			if(attrs == null || attrs.Length == 0)
+				return;
+			
+			foreach(SupportedMimeType attr in attrs)
+				file_types.Add(attr.MimeType, type);
+		}
+		
+		/// <summary>
+		///    Gets a dictionary containing all the supported mime-types
+		///    and file classes used by <see cref="File.Create(string)"
+		///    />.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IDictionary`2" /> object containing the
+		///    supported mime-types.
+		/// </value>
+		public static IDictionary<string, Type> AvailableTypes {
+			get {return file_types;}
+		}
+	}
+}
+
diff --git a/lib/TagLib/TagLib/Flac/Block.cs b/lib/TagLib/TagLib/Flac/Block.cs
new file mode 100644
index 0000000..e602275
--- /dev/null
+++ b/lib/TagLib/TagLib/Flac/Block.cs
@@ -0,0 +1,200 @@
+//
+// Block.cs: Represents a Flac metadata block.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Flac {
+	/// <summary>
+	///    This class represents a Flac metadata block.
+	/// </summary>
+	public class Block
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the block header.
+		/// </summary>
+		private BlockHeader header;
+		
+		/// <summary>
+		///    Contains the block data.
+		/// </summary>
+		private ByteVector data;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Block" /> with a specified header and internal
+		///    data.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BlockHeader" /> object containing the
+		///    header to use for the new instance.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the data
+		///    to be contained in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The size of <paramref name="data" /> does not match the
+		///    size specified in <paramref name="header" />.
+		/// </exception>
+		public Block (BlockHeader header, ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (header.BlockSize != data.Count)
+				throw new CorruptFileException (
+					"Data count not equal to block size.");
+			
+			this.header = header;
+			this.data = data;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Block" /> with of a specified type and internal
+		///    data.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="BlockType" /> value indicating the type of
+		///    data stored in <paramref name="data" />.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the data
+		///    to be contained in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public Block (BlockType type, ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			header = new BlockHeader (type, (uint) data.Count);
+			
+			this.data = data;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the type of data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="BlockType" /> value indicating the type of
+		///    data contained in <see cref="Data" />.
+		/// </value>
+		public BlockType Type {
+			get {return header.BlockType;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the block represented by the current
+		///    instance is the last metadata block in the Flac stream.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the block represented by the
+		///    current instance was the last one to appear in the file
+		///    and is followed immediately by the audio data, or <see
+		///    langword="false" /> if another block appears after the
+		///    current one or the block was not read from disk.
+		/// </value>
+		public bool IsLastBlock {
+			get {return header.IsLastBlock;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the data contained in the current
+		///    instance.
+		/// </summary>
+		public uint DataSize {
+			get {return header.BlockSize;}
+		}
+		
+		/// <summary>
+		///    Gets the total size of the block represented by the
+		///    current instance as it appears on disk.
+		/// </summary>
+		public uint TotalSize {
+			get {return DataSize + BlockHeader.Size;}
+		}
+		
+		/// <summary>
+		///    Gets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    stored in the current instance.
+		/// </value>
+		public ByteVector Data {
+			get {return data;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw Flac metadata
+		///    block.
+		/// </summary>
+		/// <param name="isLastBlock">
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    block is to be marked as the last metadata block.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render (bool isLastBlock)
+		{
+			if (this.data == null)
+				throw new InvalidOperationException (
+					"Cannot render empty blocks.");
+			
+			ByteVector data = header.Render (isLastBlock);
+			data.Add (this.data);
+			return data;
+		}
+		
+		#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Flac/BlockHeader.cs b/lib/TagLib/TagLib/Flac/BlockHeader.cs
new file mode 100644
index 0000000..d70381f
--- /dev/null
+++ b/lib/TagLib/TagLib/Flac/BlockHeader.cs
@@ -0,0 +1,197 @@
+//
+// BlockHeader.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// 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
+//
+
+using System.Collections.Generic;
+using System;
+
+namespace TagLib.Flac
+{
+	/// <summary>
+	///    Specifies the contents of a Flac block in <see cref="BlockHeader"
+	///    />.
+	/// </summary>
+	public enum BlockType
+	{
+		/// <summary>
+		///    The block contains stream information.
+		/// </summary>
+		StreamInfo = 0,
+		
+		/// <summary>
+		///    The block contains padding.
+		/// </summary>
+		Padding,
+		
+		/// <summary>
+		///    The block contains application data.
+		/// </summary>
+		Application,
+		
+		/// <summary>
+		///    The block contains seek table.
+		/// </summary>
+		SeekTable,
+		
+		/// <summary>
+		///    The block contains a Xipp comment.
+		/// </summary>
+		XiphComment,
+		
+		/// <summary>
+		///    The block contains a cue sheet.
+		/// </summary>
+		CueSheet,
+		
+		/// <summary>
+		///    The block contains a picture.
+		/// </summary>
+		Picture
+	}
+	
+	/// <summary>
+	///    This structure provides a representation of a Flac metadata block
+	///    header structure.
+	/// </summary>
+	public struct BlockHeader
+	{
+		/// <summary>
+		///    Contains the block type.
+		/// </summary>
+		private BlockType block_type;
+		
+		/// <summary>
+		///    Indicates whether or not this is the last metadata block.
+		/// </summary>
+		private bool is_last_block;
+		
+		/// <summary>
+		///    Contains the block size.
+		/// </summary>
+		private uint block_size;
+		
+		/// <summary>
+		///    The size of a block header.
+		/// </summary>
+		public const uint Size = 4;
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="BlockHeader" /> by reading a raw header from a <see
+		///    cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing a raw
+		///    block header.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 4 bytes.
+		/// </exception>
+		public BlockHeader (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"Not enough data in Flac header.");
+			
+			block_type = (BlockType) (data[0] & 0x7f);
+			is_last_block = (data[0] & 0x80) != 0;
+			block_size = data.Mid (1,3).ToUInt ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="BlockHeader" /> for a specified block type and size.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="BlockType" /> value describing the contents
+		///    of the block.
+		/// </param>
+		/// <param name="blockSize">
+		///    A <see cref="uint" /> value containing the block data
+		///    size minus the size of the header.
+		/// </param>
+		public BlockHeader (BlockType type, uint blockSize)
+		{
+			block_type    = type;
+			is_last_block = false;
+			block_size    = blockSize;
+		}
+		
+		/// <summary>
+		///    Renderes the current instance as a raw Flac block header.
+		/// </summary>
+		/// <param name="isLastBlock">
+		///    A <see cref="bool" /> value specifying whether or not the
+		///    header is the last header of the file.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered header.
+		/// </returns>
+		public ByteVector Render (bool isLastBlock)
+		{
+			ByteVector data = ByteVector.FromUInt (block_size);
+			data [0] = (byte)(block_type + (isLastBlock ? 0x80 : 0));
+			return data;
+		}
+		
+		/// <summary>
+		///    Gets the type of block described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="BlockType" /> value describing the block
+		///    type.
+		/// </value>
+		public BlockType BlockType {
+			get {return block_type;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the block is the last in the file.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the block is the last in the
+		///    file; otherwise <see langword="false" />.
+		/// </value>
+		public bool IsLastBlock {
+			get {return is_last_block;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the block described by the current
+		///    instance, minus the block header.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    block, minus the header.
+		/// </value>
+		public uint BlockSize {
+			get {return block_size;}
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Flac/File.cs b/lib/TagLib/TagLib/Flac/File.cs
new file mode 100644
index 0000000..811999c
--- /dev/null
+++ b/lib/TagLib/TagLib/Flac/File.cs
@@ -0,0 +1,682 @@
+//
+// File.cs: Provides tagging and properties support for Xiph's Flac audio files.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   flacfile.cpp from TagLib
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// Copyright (C) 2003-2004 Allan Sandfeld Jensen (Original 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Flac {
+	/// <summary>
+	///    This class extends <see cref="TagLib.NonContainer.File" /> to
+	///    provide tagging and properties support for Xiph's Flac audio
+	///    files.
+	/// </summary>
+	/// <remarks>
+	///    A <see cref="TagLib.Ogg.XiphComment" /> will be added
+	///    automatically to any file that doesn't contain one. This change
+	///    does not effect the physical file until <see cref="Save" /> is
+	///    called and can be reversed using the following method:
+	///    <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
+	/// </remarks>
+	[SupportedMimeType("taglib/flac", "flac")]
+	[SupportedMimeType("audio/x-flac")]
+	[SupportedMimeType("application/x-flac")]
+	[SupportedMimeType("audio/flac")]
+	public class File : TagLib.NonContainer.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the Flac metadata tag.
+		/// </summary>
+		private Metadata metadata = null;
+		
+		/// <summary>
+		///    Contains the combination of all file tags.
+		/// </summary>
+		private CombinedTag tag = null;
+		
+		/// <summary>
+		///    Contains the Flac header block.
+		/// </summary>
+		private ByteVector header_block = null;
+		
+		/// <summary>
+		///    Contains the stream start position.
+		/// </summary>
+		private long stream_start = 0;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path)
+			: base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle)
+			: base (abstraction, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: base (abstraction)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override TagLib.Tag Tag {
+			get {return tag;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			Mode = AccessMode.Write;
+			try {
+				// Update the tags at the beginning of the file.
+				long metadata_start = StartTag.Write ();
+				long metadata_end;
+				
+				// Get all the blocks, but don't read the data for ones
+				// we're filling with stored data.
+				IList<Block> old_blocks = ReadBlocks (ref metadata_start,
+					out metadata_end, BlockMode.Blacklist,
+					BlockType.XiphComment, BlockType.Picture);
+				
+				// Create new vorbis comments is they don't exist.
+				GetTag (TagTypes.Xiph, true);
+				
+				// Create new blocks and add the basics.
+				List<Block> new_blocks = new List<Block> ();
+				new_blocks.Add (old_blocks [0]);
+				
+				// Add blocks we don't deal with from the file.
+				foreach (Block block in old_blocks)
+					if (block.Type != BlockType.StreamInfo &&
+						block.Type != BlockType.XiphComment &&
+						block.Type != BlockType.Picture &&
+						block.Type != BlockType.Padding)
+						new_blocks.Add (block);
+				
+				new_blocks.Add (new Block (BlockType.XiphComment,
+					(GetTag (TagTypes.Xiph, true) as
+						Ogg.XiphComment).Render (false)));
+				
+				foreach (IPicture picture in metadata.Pictures) {
+					if (picture == null)
+						continue;
+					
+					new_blocks.Add (new Block (BlockType.Picture,
+						new Picture (picture).Render ()));
+				}
+				
+				// Get the length of the blocks.
+				long length = 0;
+				foreach (Block block in new_blocks)
+					length += block.TotalSize;
+				
+				// Find the padding size to avoid trouble. If that fails
+				// make some.
+				long padding_size = metadata_end - metadata_start -
+					BlockHeader.Size - length;
+				if (padding_size < 0)
+					padding_size = 1024 * 4;
+				
+				// Add a padding block.
+				if (padding_size != 0)
+					new_blocks.Add (new Block (BlockType.Padding,
+						new ByteVector ((int) padding_size)));
+				
+				// Render the blocks.
+				ByteVector block_data = new ByteVector ();
+				for (int i = 0; i < new_blocks.Count; i ++)
+					block_data.Add (new_blocks [i].Render (
+						i == new_blocks.Count - 1));
+				
+				// Update the blocks.
+				Insert (block_data, metadata_start, metadata_end -
+					metadata_start);
+				
+				// Update the tags at the end of the file.
+				EndTag.Write ();
+				
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			switch (type)
+			{
+			case TagTypes.Xiph:
+				return metadata.GetComment (create, tag);
+				
+			case TagTypes.FlacMetadata:
+				return metadata;
+			}
+			
+			Tag t = (base.Tag as TagLib.NonContainer.Tag).GetTag (type);
+			
+			if (t != null || !create)
+				return t;
+			
+			switch (type)
+			{
+			case TagTypes.Id3v1:
+				return EndTag.AddTag (type, Tag);
+				
+			case TagTypes.Id3v2:
+				return StartTag.AddTag (type, Tag);
+				
+			case TagTypes.Ape:
+				return EndTag.AddTag (type, Tag);
+				
+			default:
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags (TagTypes types)
+		{
+			if ((types & TagTypes.Xiph) != 0)
+				metadata.RemoveComment ();
+			
+			if ((types & TagTypes.FlacMetadata) != 0)
+				metadata.Clear ();
+			
+			base.RemoveTags (types);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadStart (long start,
+		                                   ReadStyle propertiesStyle)
+		{
+			long end;
+			IList<Block> blocks = ReadBlocks (ref start, out end,
+				BlockMode.Whitelist, BlockType.StreamInfo,
+				BlockType.XiphComment, BlockType.Picture);
+			metadata = new Metadata (blocks);
+			
+			TagTypesOnDisk |= metadata.TagTypes;
+			
+			if (propertiesStyle != ReadStyle.None) {
+				// Check that the first block is a
+				// METADATA_BLOCK_STREAMINFO.
+				if (blocks.Count == 0 ||
+					blocks [0].Type != BlockType.StreamInfo)
+					throw new CorruptFileException (
+						"FLAC stream does not begin with StreamInfo.");
+				
+				// The stream exists from the end of the last
+				// block to the end of the file.
+				stream_start = end;
+				header_block = blocks [0].Data;
+			}
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadEnd (long end,
+		                                 ReadStyle propertiesStyle)
+		{
+			tag = new CombinedTag (metadata, base.Tag);
+			GetTag (TagTypes.Xiph, true);
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		protected override Properties ReadProperties (long start,
+		                                              long end,
+		                                              ReadStyle propertiesStyle)
+		{
+			StreamHeader header = new StreamHeader (header_block,
+				end - stream_start);
+			return new Properties (TimeSpan.Zero, header);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Indicates whether or not the block types passed into
+		///    <see cref="ReadBlocks" /> are to be white-listed or
+		///    black-listed.
+		/// </summary>
+		private enum BlockMode
+		{
+			/// <summary>
+			///    All block types except those provided are to be
+			///    returned.
+			/// </summary>
+			Blacklist,
+			
+			/// <summary>
+			///    Only those block types provides should be
+			///    returned.
+			/// </summary>
+			Whitelist
+		}
+		
+		/// <summary>
+		///    Reads all metadata blocks starting from the current
+		///    instance, starting at a specified position.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value reference specifying the
+		///    position at which to start searching for the blocks. This
+		///    will be updated to the position of the first block.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value reference updated to the
+		///    position at which the last block ends.
+		/// </param>
+		/// <param name="mode">
+		///    A <see cref="BlockMode" /> value indicating whether to
+		///    white-list or black-list the contents of <paramref
+		///    name="types" />.
+		/// </param>
+		/// <param name="types">
+		///    A <see cref="BlockType[]" /> containing the types to look
+		///    for or not look for as specified by <paramref name="mode"
+		///    />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IList`1" /> object containing the blocks
+		///    read from the current instance.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    "<c>fLaC</c>" could not be found.
+		/// </exception>
+		private IList<Block> ReadBlocks (ref long start, out long end,
+		                                 BlockMode mode,
+		                                 params BlockType[] types)
+		{
+			List<Block> blocks = new List<Block> ();
+			
+			long start_position = Find ("fLaC", start);
+			
+			if (start_position < 0)
+				throw new CorruptFileException (
+					"FLAC stream not found at starting position.");
+			
+			end = start = start_position + 4;
+			
+			Seek (start);
+			
+			BlockHeader header;
+			
+			do {
+				header = new BlockHeader (ReadBlock ((int)
+					BlockHeader.Size));
+				
+				bool found = false;
+				foreach (BlockType type in types)
+					if (header.BlockType == type) {
+						found = true;
+						break;
+					}
+				
+				if ((mode == BlockMode.Whitelist && found) ||
+					(mode == BlockMode.Blacklist && !found))
+					blocks.Add (new Block (header,
+						ReadBlock ((int)
+							header.BlockSize)));
+				else
+					Seek (header.BlockSize,
+						System.IO.SeekOrigin.Current);
+				
+				end += header.BlockSize + BlockHeader.Size;
+			} while (!header.IsLastBlock);
+			
+			return blocks;
+		}
+		
+		#endregion
+	}
+	
+	
+	
+	/// <summary>
+	///    This class extends <see cref="CombinedTag" /> to provide support
+	///    for reading and writing FLAC metadata boxes.
+	/// </summary>
+	/// <remarks>
+	///    At this point, only Xiph Comments and pictures are supported.
+	/// </remarks>
+	public class Metadata : CombinedTag
+	{
+		/// <summary>
+		///    Contains the pictures.
+		/// </summary>
+		private List<IPicture> pictures = new List<IPicture>();
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Metadata" /> using a collection of blocks.
+		/// </summary>
+		/// <param name="blocks">
+		///    A <see cref="T:System.Collections.Generic.List`1" /> object containing <see
+		///    cref="Block" /> objects to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="blocks" /> is <see langword="null" />.
+		/// </exception>
+		[Obsolete("Use Metadata(IEnumerable<Block>)")]
+		public Metadata (List<Block> blocks)
+			: this (blocks as IEnumerable<Block>)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Metadata" /> using a collection of blocks.
+		/// </summary>
+		/// <param name="blocks">
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating <see
+		///    cref="Block" /> objects to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="blocks" /> is <see langword="null" />.
+		/// </exception>
+		public Metadata (IEnumerable<Block> blocks)
+		{
+			if (blocks == null)
+				throw new ArgumentNullException ("blocks");
+			
+			foreach (Block block in blocks) {
+				if (block.Data.Count == 0)
+					continue;
+				
+				if (block.Type == BlockType.XiphComment)
+					AddTag (new Ogg.XiphComment (block.Data));
+				else if (block.Type == BlockType.Picture)
+					pictures.Add (new Picture (block.Data));
+			}
+		}
+		
+		/// <summary>
+		///    Gets the first Xiph comment stored in the current
+		///    instance, optionally creating one if necessary.
+		/// </summary>
+		/// <param name="create">
+		///    A <see cref="bool" /> value indicating whether or not a
+		///    comment should be added if one cannot be found.
+		/// </param>
+		/// <param name="copy">
+		///    A <see cref="Tag" /> object containing the source tag to
+		///    copy the values from, or <see langword="null" /> to not
+		///    copy values.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Ogg.XiphComment" /> object containing the
+		///    tag that was found in or added to the current instance.
+		///    If no matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public Ogg.XiphComment GetComment (bool create, Tag copy)
+		{
+			foreach (Tag t in Tags)
+				if (t is Ogg.XiphComment)
+					return t as Ogg.XiphComment;
+			
+			if (!create)
+				return null;
+			
+			Ogg.XiphComment c = new Ogg.XiphComment ();
+			
+			if (copy != null)
+				copy.CopyTo (c, true);
+			
+			AddTag (c);
+			
+			return c;
+		}
+		
+		/// <summary>
+		///    Removes all child Xiph Comments from the current
+		///    instance.
+		/// </summary>
+		public void RemoveComment ()
+		{
+			Ogg.XiphComment c;
+			
+			while ((c = GetComment (false, null)) != null)
+				RemoveTag (c);
+		}
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing the tag types stored in the current instance.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.FlacMetadata | base.TagTypes;}
+		}
+		
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		public override IPicture[] Pictures {
+			get {return pictures.ToArray ();}
+			set {
+				pictures.Clear ();
+				if (value != null)
+					pictures.AddRange (value);
+			}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			pictures.Clear ();
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Flac/Picture.cs b/lib/TagLib/TagLib/Flac/Picture.cs
new file mode 100644
index 0000000..9059342
--- /dev/null
+++ b/lib/TagLib/TagLib/Flac/Picture.cs
@@ -0,0 +1,332 @@
+//
+// Picture.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Flac
+{
+	/// <summary>
+	///    This class implements <see cref="IPicture" /> to provide support
+	///    for reading and writing Flac picture metadata.
+	/// </summary>
+	public class Picture : IPicture
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the picture type.
+		/// </summary>
+		private PictureType type;
+		
+		/// <summary>
+		///    Contains the mime-type.
+		/// </summary>
+		private string mime_type;
+		
+		/// <summary>
+		///    Contains the description.
+		/// </summary>
+		private string description;
+		
+		/// <summary>
+		///    Contains the width.
+		/// </summary>
+		private int width = 0;
+		
+		/// <summary>
+		///    Contains the height.
+		/// </summary>
+		private int height = 0;
+		
+		/// <summary>
+		///    Contains the color depth.
+		/// </summary>
+		private int color_depth = 0;
+		
+		/// <summary>
+		///    Contains the number of indexed colors.
+		/// </summary>
+		private int indexed_colors = 0;
+		
+		/// <summary>
+		///    Contains the picture data.
+		/// </summary>
+		private ByteVector picture_data;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Picture" /> by reading the contents of a raw Flac
+		///    image structure.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    Flac image.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 32 bytes.
+		/// </exception>
+		public Picture (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < 32)
+				throw new CorruptFileException (
+					"Data must be at least 32 bytes long");
+			
+			int pos = 0;
+			type = (PictureType) data.Mid (pos, 4).ToUInt ();
+			pos += 4;
+			
+			int mimetype_length = (int) data.Mid (pos, 4).ToUInt ();
+			pos += 4;
+			
+			mime_type = data.ToString (StringType.Latin1, pos,
+				mimetype_length);
+			pos += mimetype_length;
+			
+			int description_length = (int) data.Mid (pos, 4)
+				.ToUInt ();
+			pos += 4;
+			
+			description = data.ToString (StringType.UTF8, pos,
+				description_length);
+			pos += description_length;
+			
+			width = (int) data.Mid (pos, 4).ToUInt ();
+			pos += 4;
+			
+			height = (int) data.Mid (pos, 4).ToUInt ();
+			pos += 4;
+			
+			color_depth = (int) data.Mid (pos, 4).ToUInt ();
+			pos += 4;
+			
+			indexed_colors = (int) data.Mid (pos, 4).ToUInt ();
+			pos += 4;
+			
+			int data_length = (int) data.Mid (pos, 4).ToUInt ();
+			pos += 4;
+			
+			picture_data = data.Mid (pos, data_length);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Picture" /> by copying the properties of a <see
+		///    cref="IPicture" /> object.
+		/// </summary>
+		/// <param name="picture">
+		///    A <see cref="IPicture" /> object to use for the new
+		///    instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="picture" /> is <see langword="null" />.
+		/// </exception>
+		public Picture (IPicture picture)
+		{
+			if (picture == null)
+				throw new ArgumentNullException ("picture");
+			
+			type = picture.Type;
+			mime_type = picture.MimeType;
+			description = picture.Description;
+			picture_data = picture.Data;
+			
+			TagLib.Flac.Picture flac_picture =
+				picture as TagLib.Flac.Picture;
+			
+			if (flac_picture == null)
+				return;
+			
+			width = flac_picture.Width;
+			height = flac_picture.Height;
+			color_depth = flac_picture.ColorDepth;
+			indexed_colors = flac_picture.IndexedColors;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw Flac picture.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			
+			data.Add (ByteVector.FromUInt ((uint) Type));
+			
+			ByteVector mime_data = ByteVector.FromString (MimeType,
+				StringType.Latin1);
+			data.Add (ByteVector.FromUInt ((uint) mime_data.Count));
+			data.Add (mime_data);
+			
+			ByteVector decription_data = ByteVector.FromString (
+				Description, StringType.UTF8);
+			data.Add (ByteVector.FromUInt ((uint)
+				decription_data.Count));
+			data.Add (decription_data);
+			
+			data.Add (ByteVector.FromUInt ((uint) Width));
+			data.Add (ByteVector.FromUInt ((uint) Height));
+			data.Add (ByteVector.FromUInt ((uint) ColorDepth));
+			data.Add (ByteVector.FromUInt ((uint) IndexedColors));
+			
+			data.Add (ByteVector.FromUInt ((uint) Data.Count));
+			data.Add (Data);
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the mime-type of the picture data
+		///    stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the mime-type
+		///    of the picture data stored in the current instance.
+		/// </value>
+		public string MimeType {
+			get {return mime_type;}
+			set {mime_type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the type of content visible in the picture
+		///    stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="PictureType" /> containing the type of
+		///    content visible in the picture stored in the current
+		///    instance.
+		/// </value>
+		public PictureType Type {
+			get {return type;}
+			set {type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets a description of the picture stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the picture stored in the current instance.
+		/// </value>
+		public string Description {
+			get {return description;}
+			set {description = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the picture data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the picture
+		///    data stored in the current instance.
+		/// </value>
+		public ByteVector Data {
+			get {return picture_data;}
+			set {picture_data = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the width of the picture in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing width of the
+		///    picture stored in the current instance.
+		/// </value>
+		public int Width {
+			get {return width;}
+			set {width = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the height of the picture in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing height of the
+		///    picture stored in the current instance.
+		/// </value>
+		public int Height {
+			get {return height;}
+			set {height = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the color depth of the picture in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing color depth of the
+		///    picture stored in the current instance.
+		/// </value>
+		public int ColorDepth {
+			get {return color_depth;}
+			set {color_depth = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of indexed colors in the picture
+		///    in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing number of indexed
+		///    colors in the picture, or zero if the picture is not
+		///    stored in an indexed format.
+		/// </value>
+		public int IndexedColors {
+			get {return indexed_colors;}
+			set {indexed_colors = value;}
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Flac/StreamHeader.cs b/lib/TagLib/TagLib/Flac/StreamHeader.cs
new file mode 100644
index 0000000..658c91d
--- /dev/null
+++ b/lib/TagLib/TagLib/Flac/StreamHeader.cs
@@ -0,0 +1,237 @@
+//
+// StreamHeader.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   flagproperties.cpp from TagLib
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// Copyright (C) 2003 Allan Sandfeld Jensen (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Flac
+{
+	/// <summary>
+	///    This structure implements <see cref="IAudioCodec" /> and provides
+	///    information about a Flac audio stream.
+	/// </summary>
+	public struct StreamHeader : IAudioCodec, ILosslessAudioCodec
+	{
+#region Private Properties
+		
+		/// <summary>
+		///    Contains the flags.
+		/// </summary>
+		private uint flags;
+		
+		/// <summary>
+		///    Contains the low portion of the length.
+		/// </summary>
+		private uint low_length;
+		
+		/// <summary>
+		///    Contains the stream length.
+		/// </summary>
+		private long stream_length;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StreamHeader" /> by reading a raw stream header
+		///    structure and using the stream length.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    stream header.
+		/// </param>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value containing the length of the
+		///    stream.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 18 bytes.
+		/// </exception>
+		public StreamHeader (ByteVector data, long streamLength)
+		{
+			if (data == null)
+			throw new ArgumentNullException ("data");
+			
+			if (data.Count < 18)
+				throw new CorruptFileException (
+				"Not enough data in FLAC header.");
+			
+			this.stream_length = streamLength;
+			this.flags = data.Mid (10, 4).ToUInt (true);
+			low_length = data.Mid (14, 4).ToUInt (true);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		public TimeSpan Duration {
+			get {
+				return (AudioSampleRate > 0 && stream_length > 0)
+					? TimeSpan.FromSeconds (
+						(double) low_length /
+						(double) AudioSampleRate +
+						(double) HighLength) :
+					TimeSpan.Zero;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				return  (int) (Duration > TimeSpan.Zero ?
+					((stream_length * 8L) /
+					Duration.TotalSeconds) / 1000 : 0);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {return (int) (flags >> 12);}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return (int) (((flags >> 9) & 7) + 1);}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets the sample width of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample width of
+		///    the audio represented by the current instance.
+		/// </value>
+		[Obsolete ("This property is depreciated, use BitsPerSample instead")]
+		public int AudioSampleWidth {
+			get {return BitsPerSample;}
+		}
+
+		/// <summary>
+		///    Gets the number of bits per sample in the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of bits
+		///    per sample in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int BitsPerSample {
+			get {return (int) (((flags >> 4) & 31) + 1);}
+		}
+
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {return "Flac Audio";}
+		}
+		
+#endregion
+		
+		
+		
+#region Private Properties
+		
+		/// <summary>
+		///    Gets a high portion of the length of the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the high portion
+		///    of the length.
+		/// </value>
+		private uint HighLength {
+			get {
+				// The last 4 bits are the most significant 4
+				// bits for the 36 bit stream length in samples.
+				// (Audio files measured in days)
+				return (uint) (AudioSampleRate > 0 ?
+					(((flags & 0xf) << 28) /
+					AudioSampleRate) << 4 : 0);
+			}
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Genres.cs b/lib/TagLib/TagLib/Genres.cs
new file mode 100644
index 0000000..0d8084d
--- /dev/null
+++ b/lib/TagLib/TagLib/Genres.cs
@@ -0,0 +1,406 @@
+//
+// Genres.cs: Provides convenience functions for converting between String
+// genres and their respective audio and video indices as used by several
+// formats.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v1genres.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib {
+	/// <summary>
+	///    This static class provides convenience functions for converting
+	///    between <see cref="string" /> genres and their respective audio
+	///    and video indices as used by several formats.
+	/// </summary>
+	public static class Genres
+	{
+		/// <summary>
+		///    Contains a list of ID3v1 audio generes.
+		/// </summary>
+		private static readonly string [] audio = {
+			"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"
+		};
+		
+		/// <summary>
+		///    Contains a list of DivX audio generes.
+		/// </summary>
+		private static readonly string [] video = new string [] {
+			"Action",
+			"Action/Adventure",
+			"Adult",
+			"Adventure",
+			"Catastrophe",
+			"Child's",
+			"Claymation",
+			"Comedy",
+			"Concert",
+			"Documentary",
+			"Drama",
+			"Eastern",
+			"Entertaining",
+			"Erotic",
+			"Extremal Sport",
+			"Fantasy",
+			"Fashion",
+			"Historical",
+			"Horror",
+			"Horror/Mystic",
+			"Humor",
+			"Indian",
+			"Informercial",
+			"Melodrama",
+			"Military & War",
+			"Music Video",
+			"Musical",
+			"Mystery",
+			"Nature",
+			"Political Satire",
+			"Popular Science",
+			"Psychological Thriller",
+			"Religion",
+			"Science Fiction",
+			"Scifi Action",
+			"Slapstick",
+			"Splatter",
+			"Sports",
+			"Thriller",
+			"Western"
+		};
+		
+		/// <summary>
+		///    Gets a list of standard audio generes.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing standard audio
+		///    genres.
+		/// </value>
+		/// <remarks>
+		///    The genres are stored in the same order and with the same
+		///    values as in the ID3v1 format.
+		/// </remarks>
+		public static string [] Audio {
+			get {return (string []) audio.Clone ();}
+		}
+		
+		/// <summary>
+		///    Gets a list of standard video generes.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing standard video
+		///    genres.
+		/// </value>
+		/// <remarks>
+		///    The genres are stored in the same order and with the same
+		///    values as in the DivX format.
+		/// </remarks>
+		public static string [] Video {
+			get {return (string []) video.Clone ();}
+		}
+		
+		/// <summary>
+		///    Gets the genre index for a specified audio genre.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    genre to look up.
+		/// </param>
+		/// <returns>
+		///    A <see cref="byte" /> value containing the index of the
+		///    genre in the audio array or 255 if it could not be found.
+		/// </returns>
+		public static byte AudioToIndex (string name)
+		{
+			for (byte i = 0; i < audio.Length; i ++)
+				if (name == audio [i])
+					return i;
+			return 255;
+		}
+		
+		/// <summary>
+		///    Gets the genre index for a specified video genre.
+		/// </summary>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name of the
+		///    genre to look up.
+		/// </param>
+		/// <returns>
+		///    A <see cref="byte" /> value containing the index of the
+		///    genre in the video array or 255 if it could not be found.
+		/// </returns>
+		public static byte VideoToIndex (string name)
+		{
+			for (byte i = 0; i < video.Length; i ++)
+				if (name == video [i])
+					return i;
+			return 255;
+		}
+		
+		/// <summary>
+		///    Gets the audio genre from its index in the array.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="byte" /> value containing the index to
+		///    aquire the genre from.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the audio genre
+		///    found at the index, or <see langword="null" /> if it does
+		///    not exist.
+		/// </returns>
+		public static string IndexToAudio (byte index)
+		{
+			return (index < audio.Length) ? audio [index] : null;
+		}
+		
+		/// <summary>
+		///    Gets the video genre from its index in the array.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="byte" /> value containing the index to
+		///    aquire the genre from.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the video genre
+		///    found at the index, or <see langword="null" /> if it does
+		///    not exist.
+		/// </returns>
+		public static string IndexToVideo (byte index)
+		{
+			return (index < video.Length) ? video [index] : null;
+		}
+		
+		/// <summary>
+		///    Gets the audio genre from its index in the array.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string" /> object, either in the format
+		///    <c>"(123)"</c> or <c>"123"</c>.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the audio genre
+		///    found at the index, or <see langword="null" /> if it does
+		///    not exist.
+		/// </returns>
+		public static string IndexToAudio (string text)
+		{
+			return IndexToAudio (StringToByte (text));
+		}
+		
+		/// <summary>
+		///    Gets the video genre from its index in the array.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string" /> object, either in the format
+		///    <c>"(123)"</c> or <c>"123"</c>.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the video genre
+		///    found at the index, or <see langword="null" /> if it does
+		///    not exist.
+		/// </returns>
+		public static string IndexToVideo (string text)
+		{
+			return IndexToVideo (StringToByte (text));
+		}
+		
+		/// <summary>
+		///    Converts a string, either in the format <c>"(123)"</c> or
+		///    <c>"123"</c> into a byte or equal numeric value.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string" /> object, either in the format
+		///    <c>"(123)"</c> or <c>"123"</c>, to be converted.
+		/// </param>
+		/// <returns>
+		///    A <see cref="byte" /> value containing the numeric value
+		///    of <paramref name="text" /> or 255 if no numeric value
+		///    could be extracted.
+		/// </returns>
+		private static byte StringToByte (string text)
+		{
+			byte value;
+			int last_pos;
+			if (text != null && text.Length > 2 && text [0] == '('
+				&& (last_pos = text.IndexOf (')')) != -1
+				&& byte.TryParse (text.Substring (1,
+					last_pos - 1), out value))
+				return value;
+			
+			if (text != null && byte.TryParse (text, out value))
+				return value;
+				
+			return 255;
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/ICodec.cs b/lib/TagLib/TagLib/ICodec.cs
new file mode 100644
index 0000000..6198ac9
--- /dev/null
+++ b/lib/TagLib/TagLib/ICodec.cs
@@ -0,0 +1,239 @@
+//
+// ICodec.cs: Provides ICodec, IAudioCodec, and IVideoCodec interfaces.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace TagLib {
+	/// <summary>
+	///    Indicates the types of media represented by a <see cref="ICodec"
+	///    /> or <see cref="Properties" /> object.
+	/// </summary>
+	/// <remarks>
+	///    These values can be bitwise combined to represent multiple media
+	///    types.
+	/// </remarks>
+	[Flags]
+	public enum MediaTypes
+	{
+		/// <summary>
+		///    No media is present.
+		/// </summary>
+		None  = 0,
+		
+		/// <summary>
+		///    Audio is present.
+		/// </summary>
+		Audio = 1,
+		
+		/// <summary>
+		///    Video is present.
+		/// </summary>
+		Video = 2,
+
+		/// <summary>
+		///    A Photo is present.
+		/// </summary>
+		Photo = 3,
+	}
+	
+	/// <summary>
+	///    This interface provides basic information, common to all media
+	///    codecs.
+	/// </summary>
+	public interface ICodec
+	{
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		TimeSpan Duration {get;}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="MediaTypes" /> containing
+		///    the types of media represented by the current instance.
+		/// </value>
+		MediaTypes MediaTypes {get;}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		string Description {get;}
+	}
+	
+	/// <summary>
+	///    This interface inherits <see cref="ICodec" /> to provide
+	///    information about an audio codec.
+	/// </summary>
+	/// <remarks>
+	///    <para>When dealing with a <see cref="ICodec" />, if <see
+	///    cref="ICodec.MediaTypes" /> contains <see cref="MediaTypes.Audio"
+	///    />, it is safe to assume that the object also inherits <see
+	///    cref="IAudioCodec" /> and can be recast without issue.</para>
+	/// </remarks>
+	public interface IAudioCodec : ICodec
+	{
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		int AudioBitrate {get;}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		int AudioSampleRate {get;}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		int AudioChannels {get;}
+	}
+
+	/// <summary>
+	///    This interface provides information specific
+	///    to lossless audio codecs.
+	/// </summary>
+	public interface ILosslessAudioCodec
+	{
+		/// <summary>
+		///    Gets the number of bits per sample in the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of bits
+		///    per sample in the audio represented by the current
+		///    instance.
+		/// </value>
+		int BitsPerSample {get;}
+	}
+
+	/// <summary>
+	///    This interface inherits <see cref="ICodec" /> to provide
+	///    information about a video codec.
+	/// </summary>
+	/// <remarks>
+	///    <para>When dealing with a <see cref="ICodec" />, if <see
+	///    cref="ICodec.MediaTypes" /> contains <see cref="MediaTypes.Video"
+	///    />, it is safe to assume that the object also inherits <see
+	///    cref="IVideoCodec" /> and can be recast without issue.</para>
+	/// </remarks>
+	public interface IVideoCodec : ICodec
+	{
+		/// <summary>
+		///    Gets the width of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the width of the
+		///    video represented by the current instance.
+		/// </value>
+		int VideoWidth  {get;}
+		
+		/// <summary>
+		///    Gets the height of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the height of the
+		///    video represented by the current instance.
+		/// </value>
+		int VideoHeight {get;}
+	}
+	
+	/// <summary>
+	///    This interface inherits <see cref="ICodec" /> to provide
+	///    information about a photo.
+	/// </summary>
+	/// <remarks>
+	///    <para>When dealing with a <see cref="ICodec" />, if <see
+	///    cref="ICodec.MediaTypes" /> contains <see cref="MediaTypes.Photo"
+	///    />, it is safe to assume that the object also inherits <see
+	///    cref="IPhotoCodec" /> and can be recast without issue.</para>
+	/// </remarks>
+	public interface IPhotoCodec : ICodec
+	{
+		/// <summary>
+		///    Gets the width of the photo represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the width of the
+		///    photo represented by the current instance.
+		/// </value>
+		int PhotoWidth  {get;}
+		
+		/// <summary>
+		///    Gets the height of the photo represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the height of the
+		///    photo represented by the current instance.
+		/// </value>
+		int PhotoHeight {get;}
+
+		/// <summary>
+		///    Gets the (format specific) quality indicator of the photo
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value indicating the quality. A value
+		///    0 means that there was no quality indicator for the format
+		///    or the file.
+		/// </value>
+		int PhotoQuality {get;}
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/ByteIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/ByteIFDEntry.cs
new file mode 100644
index 0000000..2cb7bcc
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/ByteIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// ByteIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a byte value
+	/// </summary>
+	public class ByteIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public byte Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.Byte"/> to be stored
+		/// </param>
+		public ByteIFDEntry (ushort tag, byte value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Byte;
+			count = 1;
+			
+			return Value;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/ByteVectorIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/ByteVectorIFDEntry.cs
new file mode 100644
index 0000000..8bc83b1
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/ByteVectorIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// ByteVectorIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains an BYTE value with a count > 1 (byte sequence).
+	/// </summary>
+	public class ByteVectorIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The data which is stored by the current instance
+		/// </value>
+		public ByteVector Data { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> to be stored
+		/// </param>
+		public ByteVectorIFDEntry (ushort tag, ByteVector data)
+		{
+			Tag = tag;
+			Data = data;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Byte;
+			count = (uint) Data.Count;
+			
+			return Data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/LongArrayIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/LongArrayIFDEntry.cs
new file mode 100644
index 0000000..b672dc4
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/LongArrayIFDEntry.cs
@@ -0,0 +1,89 @@
+//
+// LongArrayIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a LONG value with a count > 1
+	/// </summary>
+	public class LongArrayIFDEntry : ArrayIFDEntry<uint>
+	{
+		
+#region Constructors
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="System.UInt32[]"/> to be stored
+		/// </param>
+		public LongArrayIFDEntry (ushort tag, uint [] values) : base (tag)
+		{
+			Values = values;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public override ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Long;
+			count = (uint) Values.Length;
+			
+			ByteVector data = new ByteVector ();
+			foreach (uint value in Values)
+				data.Add (ByteVector.FromUInt (value, is_bigendian));
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/LongIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/LongIFDEntry.cs
new file mode 100644
index 0000000..feec5b5
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/LongIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// LongIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a LONG value.
+	/// </summary>
+	public class LongIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public uint Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.UInt32"/> to be stored
+		/// </param>
+		public LongIFDEntry (ushort tag, uint value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Long;
+			count = 1;
+
+			return ByteVector.FromUInt (Value, is_bigendian);
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/MakernoteIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/MakernoteIFDEntry.cs
new file mode 100644
index 0000000..050d959
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/MakernoteIFDEntry.cs
@@ -0,0 +1,273 @@
+//
+// MakernoteIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	
+	/// <summary>
+	///    An enum to represent the manufactor of the makernote
+	///    The information of the makernote types is from:
+	///    http://exiv2.org/makernote.html
+	/// </summary>
+	public enum MakernoteType {
+		
+		/// <summary>
+		///    The manufactor could not be determined
+		/// </summary>
+		Unknown,
+		
+		/// <summary>
+		///    Canon makernote.
+		///    Standard IFD without a special prefix.
+		/// </summary>
+		Canon,
+		
+		/// <summary>
+		///    Panasonic makernote.
+		///    "Panasonic\0\0\0" prefix and IFD starting at offset 12.
+		///    The next-IFD pointer is missing
+		/// </summary>
+		Panasonic,
+		
+		/// <summary>
+		///    Pentax makernote.
+		///    "AOC\0" + 2 unknown bytes as prefix. The IFD starts at
+		///    offset 6.
+		/// </summary>
+		Pentax,
+		
+		/// <summary>
+		///    Nikon makernote (type 1).
+		///    Standard IFD without a special prefix.
+		/// </summary>
+		Nikon1,
+		
+		/// <summary>
+		///    Nikon makernote (type 2).
+		///    "Nikon\0" + 2 unknown bytes prefix. The IFD starts at
+		///    offset 8.
+		/// </summary>
+		Nikon2,
+		
+		/// <summary>
+		///    Nikon makernote (type 3).
+		///    "Nikon\0" + 4 bytes with verison code + Tiff header.
+		///    The IFD starts usually at offset 18. The offsets of the IFD
+		///    are relative to start of the Tiff header (byte 10)
+		/// </summary>
+		Nikon3,
+		
+		/// <summary>
+		///    Olympus makernote (type 1).
+		///    "OLYMP\0" + 2 unknown bytes as prefix. The IFD starts at
+		///    offset 8.
+		/// </summary>
+		Olympus1,
+		
+		/// <summary>
+		///    Olympus makernote (type 2)
+		///    "OLYMPUS\0II" + 2 unknown bytes as prefix. The IFD starts at
+		///    offset 12. The offsets of the IFD are relative to the
+		///    beginning of the makernote.
+		/// </summary>
+		Olympus2,
+		
+		/// <summary>
+		///    Sony makernote (type 1).
+		///    "SONY DSC \0\0\0" as prefix. The IFD starts at offset 12. A
+		///    next-IFD pointer is missing.
+		/// </summary>
+		Sony
+	}
+	
+	
+	/// <summary>
+	///    Contains a Makernote IFD.
+	/// </summary>
+	/// <remarks>
+	///    Makernote IFDs are mostly of the same form. They start with and
+	///    Manufactor specific prefix indicating the type and contain then
+	///    a IFD structure.
+	///    It must be distinguished, where the offsets in the IFD belongs to.
+	///    For some makernotes the offset refers to the beginning of the
+	///    surrounding metadata IFD structure, for others they refer to the
+	///    start of the makernote.
+	///    In addition the endianess of the makernote can be different to the
+	///    endianess of the surrounding metadata.
+	///    This class takes care about all those things.
+	/// </remarks>
+	public class MakernoteIFDEntry : IFDEntry
+	{
+		
+#region Private Fields
+		
+		/// <value>
+		///    Stores the prefix of the makernote
+		/// </value>
+		private ByteVector prefix;
+		
+		/// <value>
+		///    Stores the offset of the IFD contained in makernote
+		/// </value>
+		private uint ifd_offset;
+		
+		/// <value>
+		///    Indicates, if the offsets are relative to the current makernote
+		///    or absolut to the base_offset of the surrounding IFD.
+		/// </value>
+		private bool absolute_offset;
+		
+		/// <value>
+		///    Stores, if the makernote is encoded in big- or little endian.
+		///    If the field is <see langword="null"/>, the endianess of the
+		///    surrounding IFD is used.
+		/// </value>
+		private bool? is_bigendian;
+		
+#endregion
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The type of the makernote the current instance represents
+		/// </value>
+		public MakernoteType MakernoteType { get; private set; }
+		
+		/// <value>
+		///    The pure <see cref="IFDStructure"/> which is stored by the
+		///    makernote.
+		/// </value>
+		public IFDStructure Structure { get; private set; }
+		
+#endregion
+
+#region Constructors
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="structure">
+		///    A <see cref="IFDStructure"/> with the IFD structure, which is stored by this
+		///    instance
+		/// </param>
+		/// <param name="makernote_type">
+		///    A <see cref="MakernoteType"/> with the type of the makernote.
+		/// </param>
+		/// <param name="prefix">
+		///    A <see cref="ByteVector"/> containing the prefix, which should be rendered
+		///    before the real IFD.
+		/// </param>
+		/// <param name="ifd_offset">
+		///    A <see cref="System.UInt32"/> with the offset in addition to the relative
+		///    offsets in the IFD
+		/// </param>
+		/// <param name="absolute_offset">
+		///    A <see cref="System.Boolean"/> indicating if the offsets of the IFD are relative
+		///    to the <paramref name="ifd_offset"/>, or absolut to the base offset of the
+		///    surrounding IFD.
+		/// </param>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Nullable"/> indicating if the current IFD is encoded in
+		///    big- or little endian. It it is <see langword="null"/>, the endianess of the
+		///    surrounding IFD is used.
+		/// </param>
+		public MakernoteIFDEntry (ushort tag, IFDStructure structure, MakernoteType makernote_type, ByteVector prefix, uint ifd_offset, bool absolute_offset, bool? is_bigendian)
+		{
+			Tag = tag;
+			Structure = structure;
+			MakernoteType = makernote_type;
+			this.prefix = prefix;
+			this.ifd_offset = ifd_offset;
+			this.absolute_offset = absolute_offset;
+			this.is_bigendian = is_bigendian;
+		}
+		
+		/// <summary>
+		///    Constructor. Creates a makernote instance just containing an IFD and
+		///    without any special prefix or offset behavior.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="structure">
+		///    A <see cref="IFDStructure"/> with the IFD structure, which is stored by this
+		///    instance
+		/// </param>
+		/// <param name="makernote_type">
+		///    A <see cref="MakernoteType"/> with the type of the makernote.
+		/// </param>
+		public MakernoteIFDEntry (ushort tag, IFDStructure structure, MakernoteType makernote_type)
+			: this (tag, structure, makernote_type, null, 0, true, null) {}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Undefined;
+			
+			var renderer =
+				new IFDRenderer (this.is_bigendian ?? is_bigendian, Structure, absolute_offset ? offset + ifd_offset : ifd_offset);
+			
+			ByteVector data = renderer.Render ();
+			data.Insert (0, prefix);
+			count = (uint) data.Count;
+			return data;
+		}
+		
+#endregion
+		
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/IFD/Entries/Rational.cs b/lib/TagLib/TagLib/IFD/Entries/Rational.cs
new file mode 100644
index 0000000..7298efe
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/Rational.cs
@@ -0,0 +1,172 @@
+//
+// Rational.cs: A structure to represent rational values by a numerator and
+// a denominator.
+//
+// Author:
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.IFD.Entries
+{
+	
+	/// <summary>
+	///    Representation of an unsigned rational value
+	/// </summary>
+	public struct Rational : IFormattable
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    The numerator of the rational value
+		/// </summary>
+		private uint numerator;
+		
+		/// <summary>
+		///    The denominator of the rational value
+		/// </summary>
+		private uint denominator;
+		
+#endregion
+
+#region Constructor
+
+		/// <summary>
+		///    Creates a new Rational value
+		/// </summary>
+		/// <param name="numerator">
+		///    A <see cref="System.UInt32"/> with the numerator of the
+		///    rational value
+		/// </param>
+		/// <param name="denominator">
+		///    A <see cref="System.UInt32"/> with the denominator of the
+		///    rational value. It must be not 0.
+		/// </param>
+		public Rational (uint numerator, uint denominator)
+		{
+			Numerator = numerator;
+			Denominator = denominator;
+		}
+		
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Returns a rational value with reduced nominator and denominator
+		/// </summary>
+		/// <returns>
+		/// A <see cref="Rational"/>
+		/// </returns>
+		public Rational Reduce ()
+		{
+			uint gcd = Denominator;
+			uint b = Numerator;
+
+			while (b != 0) {
+				uint tmp = gcd % b;
+				gcd = b;
+				b = tmp;
+			}
+			
+			return new Rational (Numerator / gcd, Denominator / gcd);
+		}
+		
+		/// <summary>
+		///    Formatprovider to allow formatting of a value. <see cref="IFormattable"/>
+		/// </summary>
+		/// <param name="format">
+		///    A <see cref="System.String"/>. <see cref="IFormattable"/>
+		/// </param>
+		/// <param name="provider">
+		///    A <see cref="IFormatProvider"/>. <see cref="IFormattable"/>
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.String"/> formated according to the given parameter
+		/// </returns>
+		public string ToString (string format, IFormatProvider provider) {
+			
+			Rational reduced = Reduce ();
+			
+			return String.Format ("{0}/{1}", reduced.Numerator, reduced.Denominator);
+		}
+		
+		/// <summary>
+		///    Converts the value to a <see cref="System.String"/>.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.String"/> with the current value.
+		/// </returns>
+		public override string ToString ()
+		{
+			return String.Format ("{0}", this);
+		}
+		
+#endregion
+
+#region Public Properties
+		
+		/// <value>
+		///    The numerator of the rational value
+		/// </value>
+		public uint Numerator {
+			get { return numerator; }
+			set { numerator = value; }
+		}
+		
+		/// <value>
+		///    The denominator of the rational value
+		/// </value>
+		/// <remarks>
+		///    Cannot be 0.
+		/// </remarks>
+		public uint Denominator {
+			get { return denominator; }
+			set {
+				if (value == 0)
+					throw new ArgumentException ("denominator");
+				
+				denominator = value;
+			}
+		}
+		
+#endregion
+
+#region Public Static Methods
+		
+		/// <summary>
+		///    Cast the <see cref="Rational"/> value to a <see cref="System.Double"/>.
+		/// </summary>
+		/// <param name="rat">
+		///    A <see cref="Rational"/> with the value to cast.
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.Double"/> with the double.
+		/// </returns>
+		public static implicit operator double (Rational rat)
+		{
+			return (double) rat.Numerator / (double) rat.Denominator;
+		}
+		
+#endregion
+			
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/RationalArrayIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/RationalArrayIFDEntry.cs
new file mode 100644
index 0000000..eb87589
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/RationalArrayIFDEntry.cs
@@ -0,0 +1,92 @@
+//
+// RationalArrayIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a RATIONAL value with a count > 1
+	/// </summary>
+	public class RationalArrayIFDEntry : ArrayIFDEntry<Rational>
+	{
+		
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="entries">
+		///    A <see cref="Rational[]"/> to be stored
+		/// </param>
+		public RationalArrayIFDEntry (ushort tag, Rational [] entries)
+			: base (tag)
+		{
+			Values = entries;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public override ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Rational;
+			count = (uint) Values.Length;
+
+			ByteVector data = new ByteVector ();
+			foreach (Rational rational in Values) {
+				data.Add (ByteVector.FromUInt (rational.Numerator, is_bigendian));
+				data.Add (ByteVector.FromUInt (rational.Denominator, is_bigendian));
+			}
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/RationalIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/RationalIFDEntry.cs
new file mode 100644
index 0000000..f85b9bf
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/RationalIFDEntry.cs
@@ -0,0 +1,104 @@
+//
+// RationalIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a RATIONAL value.
+	/// </summary>
+	public class RationalIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public Rational Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		/// A <see cref="Rational"/>
+		/// </param>
+		public RationalIFDEntry (ushort tag, Rational value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Rational;
+			count = 1;
+			
+			ByteVector data = new ByteVector ();
+			data.Add (ByteVector.FromUInt (Value.Numerator, is_bigendian));
+			data.Add (ByteVector.FromUInt (Value.Denominator, is_bigendian));
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SByteIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/SByteIFDEntry.cs
new file mode 100644
index 0000000..a07ac77
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SByteIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// SByteIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a signed byte value
+	/// </summary>
+	public class SByteIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public sbyte Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.SByte"/> to be stored
+		/// </param>
+		public SByteIFDEntry (ushort tag, sbyte value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.SByte;
+			count = 1;
+
+			return (byte) Value;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SLongArrayIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/SLongArrayIFDEntry.cs
new file mode 100644
index 0000000..51f4ea5
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SLongArrayIFDEntry.cs
@@ -0,0 +1,89 @@
+//
+// SLongArrayIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a SLONG value with a count > 1
+	/// </summary>
+	public class SLongArrayIFDEntry : ArrayIFDEntry<int>
+	{
+		
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="System.Int32[]"/> to be stored
+		/// </param>
+		public SLongArrayIFDEntry (ushort tag, int [] values) : base (tag)
+		{
+			Values = values;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public override ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Long;
+			count = (uint) Values.Length;
+			
+			ByteVector data = new ByteVector ();
+			foreach (int value in Values)
+				data.Add (ByteVector.FromInt (value, is_bigendian));
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SLongIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/SLongIFDEntry.cs
new file mode 100644
index 0000000..90a22ed
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SLongIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// SLongIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a signed LONG value.
+	/// </summary>
+	public class SLongIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public int Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.Int32"/> to be stored
+		/// </param>
+		public SLongIFDEntry (ushort tag, int value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.SLong;
+			count = 1;
+
+			return ByteVector.FromInt (Value, is_bigendian);
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SRational.cs b/lib/TagLib/TagLib/IFD/Entries/SRational.cs
new file mode 100644
index 0000000..2efff19
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SRational.cs
@@ -0,0 +1,173 @@
+//
+// SRational.cs: A structure to represent signed rational values by a
+// numerator and a denominator.
+//
+// Author:
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.IFD.Entries
+{
+	
+	/// <summary>
+	///    Representation of a signed rational value
+	/// </summary>
+	public struct SRational : IFormattable
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    The numerator of the rational value
+		/// </summary>
+		private int numerator;
+		
+		/// <summary>
+		///    The denominator of the rational value
+		/// </summary>
+		private int denominator;
+		
+#endregion
+
+#region Constructor
+
+		/// <summary>
+		///    Creates a new Rational value
+		/// </summary>
+		/// <param name="numerator">
+		///    A <see cref="System.Int32"/> with the numerator of the
+		///    rational value
+		/// </param>
+		/// <param name="denominator">
+		///    A <see cref="System.Int32"/> with the denominator of the
+		///    rational value. It must be not 0.
+		/// </param>
+		public SRational (int numerator, int denominator)
+		{
+			Numerator = numerator;
+			Denominator = denominator;
+		}
+		
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Returns a rational value with reduced nominator and denominator
+		/// </summary>
+		/// <returns>
+		///    A <see cref="SRational"/>
+		/// </returns>
+		public SRational Reduce ()
+		{
+			int den_sign = Math.Sign (Denominator);
+			int gcd = Math.Abs (Denominator);
+			int b = Math.Abs (Numerator);
+
+			while (b != 0) {
+				int tmp = gcd % b;
+				gcd = b;
+				b = tmp;
+			}
+			
+			return new SRational (den_sign * (Numerator / gcd), Math.Abs (Denominator) / gcd);
+		}
+		
+		/// <summary>
+		///    Formatprovider to allow formatting of a value. <see cref="IFormattable"/>
+		/// </summary>
+		/// <param name="format">
+		///    A <see cref="System.String"/>. <see cref="IFormattable"/>
+		/// </param>
+		/// <param name="provider">
+		///    A <see cref="IFormatProvider"/>. <see cref="IFormattable"/>
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.String"/> formated according to the given parameter
+		/// </returns>
+		public string ToString (string format, IFormatProvider provider) {
+			
+			SRational reduced = Reduce ();
+			
+			return String.Format ("{0}/{1}", reduced.Numerator, reduced.Denominator);
+		}
+				
+		/// <summary>
+		///    Converts the value to a <see cref="System.String"/>.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.String"/> with the current value.
+		/// </returns>
+		public override string ToString ()
+		{
+			return String.Format ("{0}", this);
+		}
+		
+#endregion
+
+#region Public Properties
+		
+		/// <value>
+		///    The numerator of the rational value
+		/// </value>
+		public int Numerator {
+			get { return numerator; }
+			set { numerator = value; }
+		}
+		
+		/// <value>
+		///    The denominator of the rational value
+		/// </value>
+		/// <remarks>
+		///    Cannot be 0.
+		/// </remarks>
+		public int Denominator {
+			get { return denominator; }
+			set {
+				if (value == 0)
+					throw new ArgumentException ("denominator");
+				
+				denominator = value;
+			}
+		}
+		
+#endregion
+
+#region Public Static Methods
+		
+		/// <summary>
+		///    Cast the <see cref="Rational"/> value to a <see cref="System.Double"/>.
+		/// </summary>
+		/// <param name="rat">
+		///    A <see cref="Rational"/> with the value to cast.
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.Double"/> with the double.
+		/// </returns>
+		public static implicit operator double (SRational rat)
+		{
+			return (double) rat.Numerator / (double) rat.Denominator;
+		}
+		
+#endregion
+			
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SRationalIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/SRationalIFDEntry.cs
new file mode 100644
index 0000000..c6f3cdf
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SRationalIFDEntry.cs
@@ -0,0 +1,104 @@
+//
+// SRationalIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a SRATIONAL value.
+	/// </summary>
+	public class SRationalIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public SRational Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="SRational"/> to be stored
+		/// </param>
+		public SRationalIFDEntry (ushort tag, SRational value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.SRational;
+			count = 1;
+			
+			ByteVector data = new ByteVector ();
+			data.Add (ByteVector.FromInt (Value.Numerator, is_bigendian));
+			data.Add (ByteVector.FromInt (Value.Denominator, is_bigendian));
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SShortArrayIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/SShortArrayIFDEntry.cs
new file mode 100644
index 0000000..91f9eb3
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SShortArrayIFDEntry.cs
@@ -0,0 +1,89 @@
+//
+// SShortArrayIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a SSHORT value with a count > 1
+	/// </summary>
+	public class SShortArrayIFDEntry : ArrayIFDEntry<short>
+	{
+		
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="System.Int16[]"/> to be stored
+		/// </param>
+		public SShortArrayIFDEntry (ushort tag, short [] values) : base (tag)
+		{
+			Values = values;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public override ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.SShort;
+			count = (uint) Values.Length;
+			
+			ByteVector data = new ByteVector ();
+			foreach (ushort value in Values)
+				data.Add (ByteVector.FromUShort ((ushort) value, is_bigendian));
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SShortIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/SShortIFDEntry.cs
new file mode 100644
index 0000000..9f96cca
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SShortIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// SShortIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a Signed SHORT value.
+	/// </summary>
+	public class SShortIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public short Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.Int16"/> to be stored
+		/// </param>
+		public SShortIFDEntry (ushort tag, short value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.SShort;
+			count = 1;
+
+			return ByteVector.FromUShort ((ushort) Value, is_bigendian);
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/ShortArrayIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/ShortArrayIFDEntry.cs
new file mode 100644
index 0000000..c50835f
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/ShortArrayIFDEntry.cs
@@ -0,0 +1,89 @@
+//
+// ShortArrayIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a SHORT value with a count > 1
+	/// </summary>
+	public class ShortArrayIFDEntry : ArrayIFDEntry<ushort>
+	{
+		
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="System.UInt16[]"/> to be stored
+		/// </param>
+		public ShortArrayIFDEntry (ushort tag, ushort [] values) : base (tag)
+		{
+			Values = values;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public override ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Short;
+			count = (uint) Values.Length;
+			
+			ByteVector data = new ByteVector ();
+			foreach (ushort value in Values)
+				data.Add (ByteVector.FromUShort (value, is_bigendian));
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/ShortIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/ShortIFDEntry.cs
new file mode 100644
index 0000000..9c1d909
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/ShortIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// ShortIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a SHORT value.
+	/// </summary>
+	public class ShortIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public ushort Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.UInt16"/> to be stored
+		/// </param>
+		public ShortIFDEntry (ushort tag, ushort value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Short;
+			count = 1;
+
+			return ByteVector.FromUShort (Value, is_bigendian);
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/StringIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/StringIFDEntry.cs
new file mode 100644
index 0000000..0c31351
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/StringIFDEntry.cs
@@ -0,0 +1,104 @@
+//
+// StringIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains an ASCII STRING value.
+	/// </summary>
+	public class StringIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public string Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.String"/> to be stored
+		/// </param>
+		public StringIFDEntry (ushort tag, string value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Ascii;
+			
+			ByteVector data = Value;
+			data.Add ("\0");
+			
+			count = (uint) data.Count;
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/StripOffsetsIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/StripOffsetsIFDEntry.cs
new file mode 100644
index 0000000..27e8e78
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/StripOffsetsIFDEntry.cs
@@ -0,0 +1,151 @@
+//
+// StripOffsetsIFDEntry.cs:
+//
+// Author:
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+using System.IO;
+
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains the offsets to the image data strips.
+	/// </summary>
+	public class StripOffsetsIFDEntry : ArrayIFDEntry<uint>
+	{
+
+#region Private Fields
+		
+		/// <value>
+		///    Store the strip length to read them before writing.
+		/// </value>
+		private uint[] byte_counts;
+		
+		/// <value>
+		///    The file the offsets belong to
+		/// </value>
+		private File file;
+		
+#endregion
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="System.UInt32[]"/> with the strip offsets.
+		/// </param>
+		/// <param name="byte_counts">
+		///    The length of the strips.
+		/// </param>
+		/// <param name="file">
+		///    The file from which the strips will be read.
+		/// </param>
+		public StripOffsetsIFDEntry (ushort tag, uint[] values, uint[] byte_counts, File file) : base (tag)
+		{
+			Values = values;
+			this.byte_counts = byte_counts;
+			this.file = file;
+			
+			if (values.Length != byte_counts.Length)
+				throw new Exception ("strip offsets and strip byte counts do not have the same length");
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public override ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			// The StripOffsets are an array of offsets, where the image data can be found.
+			// We store the offsets and behind the offsets the image data is stored. Therfore,
+			// the ByteVector data first collects the image data and the offsets itself are
+			// collected by offset_data. Then both are concatenated.
+			ByteVector data = new ByteVector ();
+			ByteVector offset_data = new ByteVector ();
+			
+			// every offset needs 4 byte, we need to reserve the bytes.
+			uint data_offset = offset + (uint) (4 * Values.Length);
+			
+			for (int i = 0; i < Values.Length; i++) {
+				uint new_offset = (uint) (data_offset + data.Count);
+				
+				file.Seek (Values[i], SeekOrigin.Begin);
+				data.Add (file.ReadBlock ((int) byte_counts[i]));
+				
+				// update strip offset data to new offset
+				Values[i] = new_offset;
+				
+				offset_data.Add (ByteVector.FromUInt (new_offset, is_bigendian));
+			}
+			
+			// If the StripOffsets only consists of one offset, this doesn't work, because this offset
+			// should be stored inside the IFD as a value. But, because of the additional image data,
+			// it is not stored there. We need to fix this, that the offset is adjusted correctly.
+			// Therefore, the offset_data is only added if it contains more than one value.
+			// Then, the offset is set correctly. (However, we need to ensure, that the image data
+			// consists at least of 4 bytes, which is probably the case every time, but to be sure ...)
+			// However, the strip offset in the array must also be adjusted, if the offset_data is ignored.
+			if (Values.Length > 1)
+				data.Insert (0, offset_data);
+			else
+				Values[0] = offset;
+			
+			while (data.Count < 4)
+				data.Add (0x00);
+			
+			// the entry is a single long entry where the value is an offset to the data
+			// the offset is automatically updated by the renderer.
+			type = (ushort) IFDEntryType.Long;
+			count = (uint) Values.Length;
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/SubIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/SubIFDEntry.cs
new file mode 100644
index 0000000..e3c4da8
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/SubIFDEntry.cs
@@ -0,0 +1,124 @@
+//
+// SubIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains a Sub IFD.
+	/// </summary>
+	public class SubIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The type of the IFD entry.
+		/// </value>
+		public ushort Type { get; private set; }
+		
+		/// <value>
+		///    The count of the IFD entry.
+		/// </value>
+		public uint Count { get; private set; }
+
+		/// <value>
+		///    The structure of the sub-ifd which is stored by the current
+		///    instance
+		/// </value>
+		public IFDStructure Structure { get; private set; }
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> with the type of the IFD entry.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the IFD entry.
+		/// </param>
+		/// <param name="structure">
+		///    A <see cref="IFDStructure"/> to be stored
+		/// </param>
+		public SubIFDEntry (ushort tag, ushort type, uint count, IFDStructure structure)
+		{
+			Tag = tag;
+			Type = type;
+			Count = count;
+			Structure = structure;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) Type;
+			count = 1;
+			
+			// Don't render empty SubIFDEntry
+			/*int sum = 0;
+			foreach (var directory in sub_ifd.Structure.Directories)
+				sum += directory.Count;
+			if (sum == 0)
+				return;
+			*/
+			
+			count = Count;
+			return new IFDRenderer (is_bigendian, Structure, offset).Render ();
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/ThumbnailDataIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/ThumbnailDataIFDEntry.cs
new file mode 100644
index 0000000..e199f78
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/ThumbnailDataIFDEntry.cs
@@ -0,0 +1,106 @@
+//
+// ThumbnailDataIFDEntry.cs:
+//
+// Author:
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains the data of a Thumbnail. Since the thumbnail is
+	///    referenced by two long entries (offset to the data and length)
+	///    we need to take care of this special case.
+	///    This entry acts as the offset-entry but holds also the
+	///    thumbail data. When rendering the entry, we have to render the
+	///    data but write a long entry.
+	/// </summary>
+	public class ThumbnailDataIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The data of the thumbnail which is stored by the current instance
+		/// </value>
+		public ByteVector Data { get; private set; }
+		
+#endregion
+
+#region Constructors
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> with the thumbnail data to be stored
+		/// </param>
+		public ThumbnailDataIFDEntry (ushort tag, ByteVector data)
+		{
+			Tag = tag;
+			Data = data;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			// the entry is a single long entry where the value is an offset to the data
+			// the offset is automatically updated by the renderer.
+			type = (ushort) IFDEntryType.Long;
+			count = 1;
+			
+			return Data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/UndefinedIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/UndefinedIFDEntry.cs
new file mode 100644
index 0000000..5a5c210
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/UndefinedIFDEntry.cs
@@ -0,0 +1,100 @@
+//
+// UndefinedIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (miek gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains an Undefined value type, represented by a byte vector
+	/// </summary>
+	public class UndefinedIFDEntry : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The data which is stored by the current instance
+		/// </value>
+		public ByteVector Data { get; private set; }
+		
+#endregion
+
+#region Constructors
+		
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> to be stored
+		/// </param>
+		public UndefinedIFDEntry (ushort tag, ByteVector data)
+		{
+			Tag = tag;
+			Data = data;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Undefined;
+			count = (uint) Data.Count;
+			
+			return Data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Entries/UserCommentIFDEntry.cs b/lib/TagLib/TagLib/IFD/Entries/UserCommentIFDEntry.cs
new file mode 100644
index 0000000..9a2dacb
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Entries/UserCommentIFDEntry.cs
@@ -0,0 +1,178 @@
+//
+// UserCommentIFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.IFD.Entries
+{
+	/// <summary>
+	///    Contains an ASCII STRING value.
+	/// </summary>
+	public class UserCommentIFDEntry : IFDEntry
+	{
+
+#region Constant Values
+		
+		/// <summary>
+		///   Marker for an ASCII-encoded UserComment tag.
+		/// </summary>
+		public static readonly ByteVector COMMENT_ASCII_CODE = new byte[] {0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00};
+
+		/// <summary>
+		///   Marker for a JIS-encoded UserComment tag.
+		/// </summary>
+		public static readonly ByteVector COMMENT_JIS_CODE = new byte[] {0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+		/// <summary>
+		///   Marker for a UNICODE-encoded UserComment tag.
+		/// </summary>
+		public static readonly ByteVector COMMENT_UNICODE_CODE = new byte[] {0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00};
+
+		/// <summary>
+		///   Marker for a UserComment tag with undefined encoding.
+		/// </summary>
+		public static readonly ByteVector COMMENT_UNDEFINED_CODE = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+#endregion
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The value which is stored by the current instance
+		/// </value>
+		public string Value { get; private set; }
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string"/> to be stored
+		/// </param>
+		public UserCommentIFDEntry (ushort tag, string value)
+		{
+			Tag = tag;
+			Value = value;
+		}
+
+		/// <summary>
+		///    Construcor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> to be stored
+		/// </param>
+		public UserCommentIFDEntry (ushort tag, ByteVector data)
+		{
+			Tag = tag;
+
+			if (data.StartsWith (COMMENT_ASCII_CODE)) {
+				Value = data.ToString (StringType.Latin1, COMMENT_ASCII_CODE.Count, data.Count - COMMENT_ASCII_CODE.Count);
+				return;
+			}
+
+			if (data.StartsWith (COMMENT_UNICODE_CODE)) {
+				Value = data.ToString (StringType.UTF8, COMMENT_UNICODE_CODE.Count, data.Count - COMMENT_UNICODE_CODE.Count);
+				return;
+			}
+				
+			// Some programs like e.g. CanonZoomBrowser inserts just the first 0x00-byte
+			// followed by 7-bytes of trash.
+			if (data.StartsWith ((byte) 0x00) && data.Count >= 8) {
+					
+				// And CanonZoomBrowser fills some trailing bytes of the comment field
+				// with '\0'. So we return only the characters before the first '\0'.
+				int term = data.Find ("\0", 8);
+				if (term != -1) {
+					Value = data.ToString (StringType.Latin1, 8, term - 8);
+				} else {
+					Value = data.ToString (StringType.Latin1, 8, data.Count - 8);
+				}
+				return;
+			}
+
+			if (data.Data.Length == 0) {
+				Value = String.Empty;
+				return;
+			}
+			
+			throw new NotImplementedException ("UserComment with other encoding than Latin1 or Unicode");
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count)
+		{
+			type = (ushort) IFDEntryType.Undefined;
+			
+			ByteVector data = new ByteVector ();
+			data.Add (COMMENT_UNICODE_CODE);
+			data.Add (ByteVector.FromString (Value, StringType.UTF8));
+			
+			count = (uint) data.Count;
+			
+			return data;
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/IFDDirectory.cs b/lib/TagLib/TagLib/IFD/IFDDirectory.cs
new file mode 100644
index 0000000..6d63878
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/IFDDirectory.cs
@@ -0,0 +1,35 @@
+//
+// IFDDirectory.cs: A dictionary grouping IFDEntries.
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System.Collections.Generic;
+
+namespace TagLib.IFD
+{
+	/// <summary>
+	///    Contains the entries in this IFD.
+	/// </summary>
+	public class IFDDirectory : Dictionary<ushort, IFDEntry>
+	{
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/IFDEntry.cs b/lib/TagLib/TagLib/IFD/IFDEntry.cs
new file mode 100644
index 0000000..042551a
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/IFDEntry.cs
@@ -0,0 +1,135 @@
+//
+// IFDEntry.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD
+{
+	/// <summary>
+	///    An IFD entry, which is a key/value pair inside an IFD.
+	/// </summary>
+	public interface IFDEntry
+	{
+		
+#region Properties
+
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		ushort Tag {
+			get;
+		}
+		
+#endregion
+		
+#region Methods
+
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count);
+		
+#endregion
+	
+	}
+
+	
+	/// <summary>
+	///    This class abstracts common stuff for array IFD entries
+	/// </summary>
+	public abstract class ArrayIFDEntry<T> : IFDEntry
+	{
+		
+#region Properties
+		
+		/// <value>
+		///    The ID of the tag, the current instance belongs to
+		/// </value>
+		public ushort Tag { get; private set; }
+		
+		/// <value>
+		///    The values stored by the current instance.
+		/// </value>
+		public T [] Values { get; protected set; }
+		
+#endregion
+		
+#region Constructors
+
+		/// <summary>
+		///    Constructor.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag ID of the entry this instance
+		///    represents
+		/// </param>
+		public ArrayIFDEntry (ushort tag)
+		{
+			Tag = tag;
+		}
+
+#endregion
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    A <see cref="System.Boolean"/> indicating the endianess for rendering.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset, the data is stored.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> the ID of the type, which is rendered
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the count of the values which are
+		///    rendered.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered data.
+		/// </returns>
+		public abstract ByteVector Render (bool is_bigendian, uint offset, out ushort type, out uint count);
+		
+#endregion
+		
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/IFDEntryType.cs b/lib/TagLib/TagLib/IFD/IFDEntryType.cs
new file mode 100644
index 0000000..d304b37
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/IFDEntryType.cs
@@ -0,0 +1,108 @@
+//
+// IFDEntryType.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD
+{
+	/// <summary>
+	///    A type indicator, which identifies how the corresponding value
+	///    field should be interpreted.
+	/// </summary>
+	public enum IFDEntryType : ushort
+	{
+		/// <summary>
+		///    Unknown (shouldn't occur)
+		/// </summary>
+		Unknown = 0,
+
+		/// <summary>
+		///    8-bit unsigned integer.
+		/// </summary>
+		Byte = 1,
+
+		/// <summary>
+		///    8-bit byte that contains a 7-bit ASCII code; the last byte
+		///    must be NUL (binary zero).
+		/// </summary>
+		Ascii = 2,
+
+		/// <summary>
+		///    16-bit (2-byte) unsigned integer.
+		/// </summary>
+		Short = 3,
+
+		/// <summary>
+		///    32-bit (4-byte) unsigned integer.
+		/// </summary>
+		Long = 4,
+
+		/// <summary>
+		///    Two LONGs: the first represents the numerator of a
+		///    fraction; the second, the denominator.
+		/// </summary>
+		Rational = 5,
+
+		/// <summary>
+		///    An 8-bit signed (twos-complement) integer.
+		/// </summary>
+		SByte = 6,
+
+		/// <summary>
+		///    An 8-bit byte that may contain anything, depending on
+		///    the definition of the field.
+		/// </summary>
+		Undefined = 7,
+
+		/// <summary>
+		///    A 16-bit (2-byte) signed (twos-complement) integer.
+		/// </summary>
+		SShort = 8,
+
+		/// <summary>
+		///    A 32-bit (4-byte) signed (twos-complement) integer.
+		/// </summary>
+		SLong = 9,
+
+		/// <summary>
+		///    Two SLONGâ??s: the first represents the numerator of a
+		///    fraction, the second the denominator.
+		/// </summary>
+		SRational = 10,
+
+		/// <summary>
+		///    Single precision (4-byte) IEEE format.
+		/// </summary>
+		Float = 11,
+
+		/// <summary>
+		///    Double precision (8-byte) IEEE format.
+		/// </summary>
+		Double = 12,
+		
+		/// <summary>
+		///    IFD
+		/// </summary>
+		IFD = 13
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/IFDReader.cs b/lib/TagLib/TagLib/IFD/IFDReader.cs
new file mode 100644
index 0000000..6bed757
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/IFDReader.cs
@@ -0,0 +1,860 @@
+//
+// IFDReader.cs: Parses TIFF IFDs and populates an IFD structure.
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+using System.IO;
+using TagLib.IFD.Entries;
+using TagLib.IFD.Makernotes;
+using TagLib.IFD.Tags;
+
+namespace TagLib.IFD
+{
+	/// <summary>
+	///     This class contains all the IFD reading and parsing code.
+	/// </summary>
+	public class IFDReader {
+
+#region Private Constants
+
+		private static readonly string PANASONIC_HEADER = "Panasonic\0\0\0";	
+		private static readonly string PENTAX_HEADER = "AOC\0";	
+		private static readonly string NIKON_HEADER = "Nikon\0";
+		private static readonly string OLYMPUS1_HEADER = "OLYMP\0";
+		private static readonly string OLYMPUS2_HEADER = "OLYMPUS\0";
+		private static readonly string SONY_HEADER = "SONY DSC \0\0\0";
+		
+#endregion		
+
+#region Protected Fields
+		
+		/// <summary>
+		///    The <see cref="File" /> where this IFD is found in.
+		/// </summary>
+		protected readonly File file;
+
+		/// <summary>
+		///    If IFD is encoded in BigEndian or not
+		/// </summary>
+		protected readonly bool is_bigendian;
+
+		/// <summary>
+		///    The IFD structure that will be populated
+		/// </summary>
+		protected readonly IFDStructure structure;
+
+		/// <summary>
+		///     A <see cref="System.Int64"/> value describing the base were the IFD offsets
+		///     refer to. E.g. in Jpegs the IFD are located in an Segment and the offsets
+		///     inside the IFD refer from the beginning of this segment. So base_offset must
+		///     contain the beginning of the segment.
+		/// </summary>
+		protected readonly long base_offset;
+
+		/// <summary>
+		///     A <see cref="System.UInt32"/> value with the beginning of the IFD relative to 
+		///     base_offset.
+		/// </summary>
+		protected readonly uint ifd_offset;
+		
+		/// <summary>
+		///    A <see cref="System.UInt32"/> with the maximal offset, which should occur in the
+		///    IFD. Greater offsets, would reference beyond the considered data.
+		/// </summary>
+		protected readonly uint max_offset;
+
+#endregion
+		
+#region Constructors
+
+		/// <summary>
+		///    Constructor. Reads an IFD from given file, using the given endianness.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File"/> to read from.
+		/// </param>
+		/// <param name="is_bigendian">
+		///     A <see cref="System.Boolean"/>, it must be true, if the data of the IFD should be
+		///     read as bigendian, otherwise false.
+		/// </param>
+		/// <param name="structure">
+		///    A <see cref="IFDStructure"/> that will be populated.
+		/// </param>
+		/// <param name="base_offset">
+		///     A <see cref="System.Int64"/> value describing the base were the IFD offsets
+		///     refer to. E.g. in Jpegs the IFD are located in an Segment and the offsets
+		///     inside the IFD refer from the beginning of this segment. So <paramref
+		///     name="base_offset"/> must contain the beginning of the segment.
+		/// </param>
+		/// <param name="ifd_offset">
+		///     A <see cref="System.UInt32"/> value with the beginning of the IFD relative to 
+		///     <paramref name="base_offset"/>.
+		/// </param>
+		/// <param name="max_offset">
+		/// 	A <see cref="System.UInt32"/> value with maximal possible offset. This is to limit
+		///     the size of the possible data;
+		/// </param>
+		public IFDReader (File file, bool is_bigendian, IFDStructure structure, long base_offset, uint ifd_offset, uint max_offset)
+		{
+			this.file = file;
+			this.is_bigendian = is_bigendian;
+			this.structure = structure;
+			this.base_offset = base_offset;
+			this.ifd_offset = ifd_offset;
+			this.max_offset = max_offset;
+		}
+
+#endregion
+
+#region Public Methods
+
+		/// <summary>
+		///    Read all IFD segments from the file.
+		/// </summary>
+		public void Read ()
+		{
+			Read (-1);
+		}
+		
+		/// <summary>
+		///    Read IFD segments from the file.
+		/// </summary>
+		/// <para>
+		///    The number of IFDs that may be read can be restricted using the count
+		///    parameter. This might be needed for fiels that have invalid next-ifd
+		///    pointers (such as some IFDs in the Nikon Makernote). This condition is
+		///    tested in the Nikon2 unit test, which contains such a file.
+		/// </para>
+		/// <param name="count">
+		///     A <see cref="System.Int32"/> with the maximal number of IFDs to read.
+		///     Passing -1 means unlimited.
+		/// </param>
+		public void Read (int count)
+		{
+			if (count == 0)
+				return;
+
+			uint next_offset = ifd_offset;
+			int i = 0;
+
+			do {
+				next_offset = ReadIFD (base_offset, next_offset, max_offset);
+			} while (next_offset > 0 && (count == -1 || ++i < count));
+		}
+
+#endregion
+
+#region Private Methods
+
+		/// <summary>
+		///    Reads an IFD from file at position <paramref name="offset"/> relative
+		///    to <paramref name="base_offset"/>.
+		/// </summary>
+		/// <param name="base_offset">
+		///    A <see cref="System.Int64"/> with the base offset which every offset
+		///    in IFD is relative to.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset of the IFD relative to
+		///    <paramref name="base_offset"/>
+		/// </param>
+		/// <param name="max_offset">
+		///    A <see cref="System.UInt32"/> with the maximal offset to consider for
+		///    the IFD.
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.UInt32"/> with the offset of the next IFD, the
+		///    offset is also relative to <paramref name="base_offset"/>
+		/// </returns>
+		private uint ReadIFD (long base_offset, uint offset, uint max_offset)
+		{
+			if (base_offset + offset > file.Length)
+				throw new Exception (String.Format ("Invalid IFD offset {0}, length: {1}", offset, file.Length));
+
+			var directory = new IFDDirectory ();
+
+			file.Seek (base_offset + offset, SeekOrigin.Begin);
+			ushort entry_count = ReadUShort ();
+
+			if (file.Tell + 12 * entry_count > base_offset + max_offset)
+				throw new Exception (String.Format ("Size of entries exceeds possible data size"));
+			
+			ByteVector entry_datas = file.ReadBlock (12 * entry_count);
+			uint next_offset = ReadUInt ();
+			
+			for (int i = 0; i < entry_count; i++) {
+				ByteVector entry_data = entry_datas.Mid (i * 12, 12);
+				
+				ushort entry_tag = entry_data.Mid (0, 2).ToUShort (is_bigendian);
+				ushort type = entry_data.Mid (2, 2).ToUShort (is_bigendian);
+				uint value_count = entry_data.Mid (4, 4).ToUInt (is_bigendian);
+				ByteVector offset_data = entry_data.Mid (8, 4);
+
+				IFDEntry entry = CreateIFDEntry (entry_tag, type, value_count, base_offset, offset_data, max_offset);
+				
+				if (entry == null)
+					continue;
+				
+				if (directory.ContainsKey (entry.Tag))
+					directory.Remove (entry.Tag);
+				
+				directory.Add (entry.Tag, entry);
+			}
+
+			FixupDirectory (base_offset, directory);
+			
+			structure.directories.Add (directory);
+			return next_offset;
+		}
+
+		/// <summary>
+		///    Creates an IFDEntry from the given values. This method is used for
+		///    every entry. Custom parsing can be hooked in by overriding the
+		///    <see cref="ParseIFDEntry(ushort,ushort,uint,long,uint)"/> method.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> with the type of the entry.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the data count of the entry.
+		/// </param>
+		/// <param name="base_offset">
+		///    A <see cref="System.Int64"/> with the base offset which every
+		///    offsets in the IFD are relative to.
+		/// </param>
+		/// <param name="offset_data">
+		///    A <see cref="ByteVector"/> containing exactly 4 byte with the data
+		///    of the offset of the entry. Since this field isn't interpreted as
+		///    an offset if the data can be directly stored in the 4 byte, we
+		///    pass the <see cref="ByteVector"/> to easier interpret it.
+		/// </param>
+		/// <param name="max_offset">
+		///    A <see cref="System.UInt32"/> with the maximal offset to consider for
+		///    the IFD.
+		/// </param>
+		/// <returns>
+		///    A <see cref="IFDEntry"/> with the given parameter.
+		/// </returns>
+		private IFDEntry CreateIFDEntry (ushort tag, ushort type, uint count, long base_offset, ByteVector offset_data, uint max_offset)
+		{
+			uint offset = offset_data.ToUInt (is_bigendian);
+			
+			// Fix the type for the IPTC tag.
+			// From http://www.awaresystems.be/imaging/tiff/tifftags/iptc.html
+			// "Often times, the datatype is incorrectly specified as LONG. "
+			if (tag == (ushort) IFDEntryTag.IPTC && type == (ushort) IFDEntryType.Long) {
+				type = (ushort) IFDEntryType.Byte;
+			}
+			
+			var ifd_entry = ParseIFDEntry (tag, type, count, base_offset, offset);
+			if (ifd_entry != null)
+				return ifd_entry;
+
+			// then handle the values stored in the offset data itself
+			if (count == 1) {
+				if (type == (ushort) IFDEntryType.Byte)
+					return new ByteIFDEntry (tag, offset_data[0]);
+				
+				if (type == (ushort) IFDEntryType.SByte)
+					return new SByteIFDEntry (tag, (sbyte)offset_data[0]);
+				
+				if (type == (ushort) IFDEntryType.Short)
+					return new ShortIFDEntry (tag, offset_data.Mid (0, 2).ToUShort (is_bigendian));
+				
+				if (type == (ushort) IFDEntryType.SShort)
+					return new SShortIFDEntry (tag, (short) offset_data.Mid (0, 2).ToUShort (is_bigendian));
+				
+				if (type == (ushort) IFDEntryType.Long)
+					return new LongIFDEntry (tag, offset_data.ToUInt (is_bigendian));
+				
+				if (type == (ushort) IFDEntryType.SLong)
+					return new SLongIFDEntry (tag, offset_data.ToInt (is_bigendian));
+				
+			}
+			
+			if (count == 2) {
+				if (type == (ushort) IFDEntryType.Short) {
+					ushort [] data = new ushort [] {
+						offset_data.Mid (0, 2).ToUShort (is_bigendian),
+						offset_data.Mid (2, 2).ToUShort (is_bigendian)
+					};
+					
+					return new ShortArrayIFDEntry (tag, data);
+				}
+				
+				if (type == (ushort) IFDEntryType.SShort) {
+					short [] data = new short [] {
+						(short) offset_data.Mid (0, 2).ToUShort (is_bigendian),
+						(short) offset_data.Mid (2, 2).ToUShort (is_bigendian)
+					};
+					
+					return new SShortArrayIFDEntry (tag, data);
+				}
+			}
+			
+			if (count <= 4) {
+				if (type == (ushort) IFDEntryType.Undefined)
+					return new UndefinedIFDEntry (tag, offset_data.Mid (0, (int)count));
+				
+				if (type == (ushort) IFDEntryType.Ascii)
+					return new StringIFDEntry (tag, offset_data.Mid (0, (int)count - 1).ToString ());
+				
+				if (type == (ushort) IFDEntryType.Byte)
+					return new ByteVectorIFDEntry (tag, offset_data.Mid (0, (int)count));
+			}
+			
+			
+			// FIXME: create correct type.
+			if (offset > max_offset)
+				return new UndefinedIFDEntry (tag, new ByteVector ()); 
+			
+			// then handle data referenced by the offset
+			file.Seek (base_offset + offset, SeekOrigin.Begin);
+			
+			if (count == 1) {
+				if (type == (ushort) IFDEntryType.Rational)
+					return new RationalIFDEntry (tag, ReadRational ());
+				
+				if (type == (ushort) IFDEntryType.SRational)
+					return new SRationalIFDEntry (tag, ReadSRational ());
+			}
+			
+			if (count > 1) {
+				if (type == (ushort) IFDEntryType.Long) {
+					uint [] data = ReadUIntArray (count);
+					
+					return new LongArrayIFDEntry (tag, data);
+				}
+				
+				if (type == (ushort) IFDEntryType.SLong) {
+					int [] data = ReadIntArray (count);
+					
+					return new SLongArrayIFDEntry (tag, data);
+				}
+				
+				if (type == (ushort) IFDEntryType.Rational) {
+					Rational[] entries = new Rational [count];
+					
+					for (int i = 0; i < count; i++)
+						entries[i] = ReadRational ();
+
+					return new RationalArrayIFDEntry (tag, entries);
+				}
+			}
+			
+			if (count > 2) {
+				if (type == (ushort) IFDEntryType.Short) {
+					ushort [] data = ReadUShortArray (count);
+
+					return new ShortArrayIFDEntry (tag, data);
+				}
+				
+				if (type == (ushort) IFDEntryType.SShort) {
+					short [] data = ReadShortArray (count);
+
+					return new SShortArrayIFDEntry (tag, data);
+				}
+			}
+			
+			if (count > 4) {
+				if (type == (ushort) IFDEntryType.Long) {
+					uint [] data = ReadUIntArray (count);
+
+					return new LongArrayIFDEntry (tag, data);
+				} 
+				
+				if (type == (ushort) IFDEntryType.Byte) {
+					ByteVector data = file.ReadBlock ((int) count);
+
+					return new ByteVectorIFDEntry (tag, data);
+				}
+				
+				if (type == (ushort) IFDEntryType.Ascii) {
+					string data = ReadAsciiString ((int) count);
+
+					return new StringIFDEntry (tag, data);
+				}
+
+				if (tag == (ushort) ExifEntryTag.UserComment) {
+					ByteVector data = file.ReadBlock ((int) count);
+
+					return new UserCommentIFDEntry (tag, data);
+				}
+				
+				if (type == (ushort) IFDEntryType.Undefined) {
+					ByteVector data = file.ReadBlock ((int) count);
+
+					return new UndefinedIFDEntry (tag, data);
+				}
+			}
+
+			if (type == (ushort) IFDEntryType.Float)
+				return null;
+			
+			// TODO: We should ignore unreadable values, erroring for now until we have sufficient coverage.
+			throw new NotImplementedException (String.Format ("Unknown type/count {0}/{1}", type, count));
+		}
+		
+		/// <summary>
+		///    Reads a 2-byte signed short from the current file.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="short" /> value containing the short read
+		///    from the current instance.
+		/// </returns>
+		private short ReadShort ()
+		{
+			return (short) file.ReadBlock (2).ToUShort (is_bigendian);
+		}
+		
+		/// <summary>
+		///    Reads a 2-byte unsigned short from the current file.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort" /> value containing the short read
+		///    from the current instance.
+		/// </returns>
+		private ushort ReadUShort ()
+		{
+			return file.ReadBlock (2).ToUShort (is_bigendian);
+		}
+		
+		/// <summary>
+		///    Reads a 4-byte int from the current file.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the int read
+		///    from the current instance.
+		/// </returns>
+		private int ReadInt ()
+		{
+			return file.ReadBlock (4).ToInt (is_bigendian);
+		}
+		
+		/// <summary>
+		///    Reads a 4-byte unsigned int from the current file.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the int read
+		///    from the current instance.
+		/// </returns>
+		private uint ReadUInt ()
+		{
+			return file.ReadBlock (4).ToUInt (is_bigendian);
+		}
+		
+		/// <summary>
+		///    Reads a <see cref="Rational"/> by two following unsigned
+		///    int from the current file.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="Rational"/> value created by the read values.
+		/// </returns>
+		private Rational ReadRational ()
+		{
+			uint numerator = ReadUInt ();
+			uint denominator = ReadUInt ();
+
+			// correct illegal value
+			if (denominator == 0) {
+				numerator = 0;
+				denominator = 1;
+			}
+			
+			return new Rational (numerator, denominator);
+		}
+		
+		/// <summary>
+		///    Reads a <see cref="SRational"/> by two following unsigned
+		///    int from the current file.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="SRational"/> value created by the read values.
+		/// </returns>
+		private SRational ReadSRational ()
+		{
+			int numerator = ReadInt ();
+			int denominator = ReadInt ();
+			
+			// correct illegal value
+			if (denominator == 0) {
+				numerator = 0;
+				denominator = 1;
+			}
+			
+			return new SRational (numerator, denominator);
+		}
+
+		/// <summary>
+		///    Reads an array of 2-byte shorts from the current file.
+		/// </summary>
+		/// <returns>
+		///    An array of <see cref="ushort" /> values containing the
+		///    shorts read from the current instance.
+		/// </returns>
+		private ushort [] ReadUShortArray (uint count)
+		{
+			ushort [] data = new ushort [count];
+			for (int i = 0; i < count; i++)
+				data [i] = ReadUShort ();
+			return data;
+		}
+		
+		/// <summary>
+		///    Reads an array of 2-byte signed shorts from the current file.
+		/// </summary>
+		/// <returns>
+		///    An array of <see cref="short" /> values containing the
+		///    shorts read from the current instance.
+		/// </returns>
+		private short [] ReadShortArray (uint count)
+		{
+			short [] data = new short [count];
+			for (int i = 0; i < count; i++)
+				data [i] = ReadShort ();
+			return data;
+		}
+		
+		/// <summary>
+		///    Reads an array of 4-byte int from the current file.
+		/// </summary>
+		/// <returns>
+		///    An array of <see cref="int" /> values containing the
+		///    shorts read from the current instance.
+		/// </returns>
+		private int [] ReadIntArray (uint count)
+		{
+			int [] data = new int [count];
+			for (int i = 0; i < count; i++)
+				data [i] = ReadInt ();
+			return data;
+		}
+		
+		/// <summary>
+		///    Reads an array of 4-byte unsigned int from the current file.
+		/// </summary>
+		/// <returns>
+		///    An array of <see cref="uint" /> values containing the
+		///    shorts read from the current instance.
+		/// </returns>
+		private uint [] ReadUIntArray (uint count)
+		{
+			uint [] data = new uint [count];
+			for (int i = 0; i < count; i++)
+				data [i] = ReadUInt ();
+			return data;
+		}
+
+		/// <summary>
+		///    Reads an ASCII string from the current file.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> read from the current instance.
+		/// </returns>
+		/// <remarks>
+		///    The exif standard allows to store multiple string separated
+		///    by '\0' in one ASCII-field. On the other hand some programs
+		///    (e.g. CanonZoomBrowser) fill some ASCII fields by trailing
+		///    '\0's.
+		///    We follow the Adobe practice as described in XMP Specification
+		///    Part 3 (Storeage in Files), and process the ASCII string only
+		///    to the first '\0'.
+		/// </remarks>
+		private string ReadAsciiString (int count)
+		{
+			// The last character is \0
+			string str = file.ReadBlock (count - 1).ToString ();
+			int term = str.IndexOf ('\0');
+			
+			if (term == -1)
+				return str;
+			else
+				return str.Substring (0, term);
+		}
+		
+		/// <summary>
+		///    Performs some fixups to a read <see cref="IFDDirectory"/>. For some
+		///    special cases multiple <see cref="IFDEntry"/> instances contained
+		///    in the directory are needed. Therfore, we do the fixups after reading the
+		///    whole directory to be sure, all entries are present.
+		/// </summary>
+		/// <param name="base_offset">
+		///    A <see cref="System.Int64"/> value with the base offset, all offsets in the
+		///    directory refers to.
+		/// </param>
+		/// <param name="directory">
+		///    A <see cref="IFDDirectory"/> instance which was read and needs fixes.
+		/// </param>
+		private void FixupDirectory (long base_offset, IFDDirectory directory)
+		{
+			// The following two entries refer to thumbnail data, where one is  the offset
+			// to the data and the other is the length. Unnaturally both are used to describe
+			// the data. So it is needed to keep both entries in sync and keep the thumbnail data
+			// for writing it back.
+			// We determine the position of the data, read it and store it in an ThumbnailDataIFDEntry
+			// which replaces the offset-entry to thumbnail data.
+			ushort offset_tag = (ushort) IFDEntryTag.JPEGInterchangeFormat;
+			ushort length_tag = (ushort) IFDEntryTag.JPEGInterchangeFormatLength;
+			if (directory.ContainsKey (offset_tag) && directory.ContainsKey (length_tag)) {
+				
+				var offset_entry = directory [offset_tag] as LongIFDEntry;
+				var length_entry = directory [length_tag] as LongIFDEntry;
+				
+				if (offset_entry != null && length_entry != null) {
+					uint offset = offset_entry.Value;
+					uint length = length_entry.Value;
+					
+					file.Seek (base_offset + offset, SeekOrigin.Begin);
+					ByteVector data = file.ReadBlock ((int) length);
+					
+					directory.Remove (offset_tag);
+					directory.Add (offset_tag, new ThumbnailDataIFDEntry (offset_tag, data));
+				}
+			}
+			
+			
+			// create a StripOffsetIFDEntry if necessary
+			ushort strip_offsets_tag = (ushort) IFDEntryTag.StripOffsets;
+			ushort strip_byte_counts_tag = (ushort) IFDEntryTag.StripByteCounts;
+			if (directory.ContainsKey (strip_offsets_tag) && directory.ContainsKey (strip_byte_counts_tag)) {
+				
+				uint [] strip_offsets = null;
+				uint [] strip_byte_counts = null;
+				
+				var strip_offsets_entry = directory [strip_offsets_tag];
+				var strip_byte_counts_entry = directory [strip_byte_counts_tag];
+				
+				if (strip_offsets_entry is LongIFDEntry)
+					strip_offsets = new uint[] {(strip_offsets_entry as LongIFDEntry).Value};
+				else if (strip_offsets_entry is LongArrayIFDEntry)
+					strip_offsets = (strip_offsets_entry as LongArrayIFDEntry).Values;
+				
+				if (strip_offsets == null)
+					return;
+				
+				if (strip_byte_counts_entry is LongIFDEntry)
+					strip_byte_counts = new uint[] {(strip_byte_counts_entry as LongIFDEntry).Value};
+				else if (strip_byte_counts_entry is LongArrayIFDEntry)
+					strip_byte_counts = (strip_byte_counts_entry as LongArrayIFDEntry).Values;
+				
+				if (strip_byte_counts == null)
+					return;
+				
+				directory.Remove (strip_offsets_tag);
+				directory.Add (strip_offsets_tag, new StripOffsetsIFDEntry (strip_offsets_tag, strip_offsets, strip_byte_counts, file));
+			}
+		}
+		
+		private IFDEntry ParseMakernote (ushort tag, ushort type, uint count, long base_offset, uint offset)
+		{
+			long makernote_offset = base_offset + offset;
+			IFDStructure ifd_structure = new IFDStructure ();
+			
+			// This is the minimum size a makernote should have
+			// The shortest header is PENTAX_HEADER (4)
+			// + IFD entry count (2)
+			// + at least one IFD etry (12)
+			// + next IFD pointer (4)
+			// = 22 ....
+			// we use this number to read a header which is big used
+			// to identify the makernote types
+			int header_size = 18;
+			
+			if (file.Length < makernote_offset)
+			    throw new Exception ("offset to makernote is beyond file size");
+			
+			if (file.Length < makernote_offset + header_size)
+				throw new Exception ("data is to short to contain a maker note ifd");
+			
+			// read header
+			file.Seek (makernote_offset, SeekOrigin.Begin);
+			ByteVector header = file.ReadBlock (header_size);
+			
+			if (header.StartsWith (PANASONIC_HEADER)) {
+				IFDReader reader =
+					new IFDReader (file, is_bigendian, ifd_structure, base_offset, offset + 12, max_offset);
+
+				reader.ReadIFD (base_offset, offset + 12, max_offset);
+				return new MakernoteIFDEntry (tag, ifd_structure, MakernoteType.Panasonic, PANASONIC_HEADER, 12, true, null);
+			}
+			
+			if (header.StartsWith (PENTAX_HEADER)) {
+				IFDReader reader =
+					new IFDReader (file, is_bigendian, ifd_structure, base_offset, offset + 6, max_offset);
+
+				reader.ReadIFD (base_offset, offset + 6, max_offset);
+				return new MakernoteIFDEntry (tag, ifd_structure, MakernoteType.Pentax, header.Mid (0, 6), 6, true, null);
+			}
+			
+			if (header.StartsWith (OLYMPUS1_HEADER)) {
+				IFDReader reader =
+					new IFDReader (file, is_bigendian, ifd_structure, base_offset, offset + 8, max_offset);
+
+				reader.Read ();
+				return new MakernoteIFDEntry (tag, ifd_structure, MakernoteType.Olympus1, header.Mid (0, 8), 8, true, null);
+			}
+			
+			if (header.StartsWith (OLYMPUS2_HEADER)) {
+				IFDReader reader =
+					new IFDReader (file, is_bigendian, ifd_structure, makernote_offset, 12, count);
+
+				reader.Read ();
+				return new MakernoteIFDEntry (tag, ifd_structure, MakernoteType.Olympus2, header.Mid (0, 12), 12, false, null);
+			}
+			
+			if (header.StartsWith (SONY_HEADER)) {
+				IFDReader reader =
+					new IFDReader (file, is_bigendian, ifd_structure, base_offset, offset + 12, max_offset);
+
+				reader.ReadIFD (base_offset, offset + 12, max_offset);
+				return new MakernoteIFDEntry (tag, ifd_structure, MakernoteType.Sony, SONY_HEADER, 12, true, null);
+			}
+			
+			if (header.StartsWith (NIKON_HEADER)) {
+				
+				ByteVector endian_bytes = header.Mid (10, 2);
+				
+				if (endian_bytes.ToString () == "II" || endian_bytes.ToString () == "MM") {
+					
+					bool makernote_endian = endian_bytes.ToString ().Equals ("MM");
+					ushort magic = header.Mid (12, 2).ToUShort (is_bigendian);
+					
+					if (magic == 42) {
+						
+						// TODO: the max_offset value is not correct here. However, some nikon files have offsets to a sub-ifd
+						// (preview image) which are not stored with the other makernote data. Therfore, we keep the max_offset
+						// for now. (It is just an upper bound for some checks. So if it is too big, it doesn't matter)
+						var reader =
+							new Nikon3MakernoteReader (file, makernote_endian, ifd_structure, makernote_offset + 10, 8, max_offset - offset - 10);
+						
+						reader.Read ();
+						return new MakernoteIFDEntry (tag, ifd_structure, MakernoteType.Nikon3, header.Mid (0, 18), 8, false, makernote_endian);
+					}
+				}
+			}
+			
+			try {
+				IFDReader reader =
+					new IFDReader (file, is_bigendian, ifd_structure, base_offset, offset, max_offset);
+
+				reader.Read ();
+				return new MakernoteIFDEntry (tag, ifd_structure, MakernoteType.Canon);
+			} catch {
+				return null;
+			}
+		}
+
+#endregion
+
+#region Protected Methods
+
+		/// <summary>
+		///    Try to parse the given IFD entry, used to discover format-specific entries.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> with the type of the entry.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the data count of the entry.
+		/// </param>
+		/// <param name="base_offset">
+		///    A <see cref="System.Int64"/> with the base offset which every offsets in the
+		///    IFD are relative to.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset of the entry.
+		/// </param>
+		/// <returns>
+		///    A <see cref="IFDEntry"/> with the given parameters, or null if none was parsed, after
+		///    which the normal TIFF parsing is used.
+		/// </returns>
+		protected virtual IFDEntry ParseIFDEntry (ushort tag, ushort type, uint count, long base_offset, uint offset)
+		{
+			if (tag == (ushort) ExifEntryTag.MakerNote)
+				return ParseMakernote (tag, type, count, base_offset, offset);
+			
+			IFDStructure ifd_structure = new IFDStructure ();
+			IFDReader reader = CreateSubIFDReader (file, is_bigendian, ifd_structure, base_offset, offset, max_offset);
+			
+			// Sub IFDs are either identified by the IFD-type ...
+			if (type == (ushort) IFDEntryType.IFD) {
+				reader.Read ();
+				return new SubIFDEntry (tag, type, (uint) ifd_structure.Directories.Length, ifd_structure);
+			}
+			
+			// ... or by one of the following tags
+			switch (tag) {
+			case (ushort) IFDEntryTag.ExifIFD:
+			case (ushort) IFDEntryTag.InteroperabilityIFD:
+			case (ushort) IFDEntryTag.GPSIFD:
+				reader.Read ();
+				return new SubIFDEntry (tag, (ushort) IFDEntryType.Long, 1, ifd_structure);
+			
+			default:
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Create a reader for Sub IFD entries.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File"/> to read from.
+		/// </param>
+		/// <param name="is_bigendian">
+		///     A <see cref="System.Boolean"/>, it must be true, if the data of the IFD should be
+		///     read as bigendian, otherwise false.
+		/// </param>
+		/// <param name="structure">
+		///    A <see cref="IFDStructure"/> that will be populated.
+		/// </param>
+		/// <param name="base_offset">
+		///    A <see cref="System.Int64"/> with the base offset which every offsets in the
+		///    IFD are relative to.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset of the entry.
+		/// </param>
+		/// <param name="max_offset">
+		///    A <see cref="System.UInt32"/> with the maximal offset to consider for
+		///    the IFD.
+		/// </param>
+		/// <returns>
+		///    A <see cref="IFDReader"/> which can be used to read the specified sub IFD.
+		/// </returns>
+		protected virtual IFDReader CreateSubIFDReader (File file, bool is_bigendian, IFDStructure structure, long base_offset, uint offset, uint max_offset)
+		{
+			return new IFDReader (file, is_bigendian, structure, base_offset, offset, max_offset);
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/IFDRenderer.cs b/lib/TagLib/TagLib/IFD/IFDRenderer.cs
new file mode 100644
index 0000000..e7204ad
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/IFDRenderer.cs
@@ -0,0 +1,267 @@
+//
+// IFDRenderer.cs: Outputs an IFD structure into TIFF IFD bytes.
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+using TagLib.IFD.Entries;
+
+namespace TagLib.IFD
+{
+	/// <summary>
+	///     This class contains all the IFD rendering code.
+	/// </summary>
+	public class IFDRenderer {
+
+#region Private Fields
+
+		/// <summary>
+		///    The IFD structure that will be rendered.
+		/// </summary>
+		private readonly IFDStructure structure;
+
+		/// <summary>
+		///    If IFD should be encoded in BigEndian or not.
+		/// </summary>
+		private readonly bool is_bigendian;
+
+		/// <summary>
+		///    A <see cref="System.UInt32"/> value with the offset of the
+		///    current IFD. All offsets inside the IFD must be adjusted
+		///    according to this given offset.
+		/// </summary>
+		private readonly uint ifd_offset;
+
+#endregion
+		
+#region Constructors
+
+		/// <summary>
+		///    Constructor. Will render the given IFD structure.
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    If IFD should be encoded in BigEndian or not.
+		/// </param>
+		/// <param name="structure">
+		///    The IFD structure that will be rendered.
+		/// </param>
+		/// <param name="ifd_offset">
+		///    A <see cref="System.UInt32"/> value with the offset of the
+		///    current IFD. All offsets inside the IFD must be adjusted
+		///    according to this given offset.
+		/// </param>
+		public IFDRenderer (bool is_bigendian, IFDStructure structure, uint ifd_offset)
+		{
+			this.is_bigendian = is_bigendian;
+			this.structure = structure;
+			this.ifd_offset = ifd_offset;
+		}
+
+#endregion
+
+#region Public Methods
+
+		/// <summary>
+		///    Renders the current instance to a <see cref="ByteVector"/>.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> containing the rendered IFD.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector ifd_data = new ByteVector ();
+
+			uint current_offset = ifd_offset;
+			var directories = structure.directories;
+			
+			for (int index = 0; index < directories.Count; index++) {
+				ByteVector data = RenderIFD (directories [index], current_offset, index == directories.Count - 1);
+				current_offset += (uint) data.Count;
+				ifd_data.Add (data);
+			}
+			
+			return ifd_data;
+		}
+
+#endregion
+
+#region Private Methods
+
+		/// <summary>
+		///    Renders the IFD to an ByteVector where the offset of the IFD
+		///    itself is <paramref name="ifd_offset"/> and all offsets
+		///    contained in the IFD are adjusted accroding it.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="IFDDirectory"/> with the directory to render.
+		/// </param>
+		/// <param name="ifd_offset">
+		///    A <see cref="System.UInt32"/> with the offset of the IFD
+		/// </param>
+		/// <param name="last">
+		///    A <see cref="System.Boolean"/> which is true, if the IFD is
+		///    the last one, i.e. the offset to the next IFD, which is
+		///    stored inside the IFD, is 0. If the value is false, the
+		///    offset to the next IFD is set that it starts directly after
+		///    the current one.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the rendered IFD.
+		/// </returns>
+		private ByteVector RenderIFD (IFDDirectory directory, uint ifd_offset, bool last)
+		{
+			if (directory.Count > (int)UInt16.MaxValue)
+				throw new Exception (String.Format ("Directory has too much entries: {0}", directory.Count));
+		
+			ushort entry_count = (ushort) directory.Count;
+			
+			// ifd_offset + size of entry_count + entries + next ifd offset
+			uint data_offset = ifd_offset + 2 + 12 * (uint) entry_count + 4;
+						
+			// store the entries itself
+			ByteVector entry_data = new ByteVector ();
+			
+			// store the data referenced by the entries
+			ByteVector offset_data = new ByteVector ();
+			
+			entry_data.Add (ByteVector.FromUShort (entry_count, is_bigendian));
+			
+			foreach (IFDEntry entry in directory.Values)
+				RenderEntryData (entry, entry_data, offset_data, data_offset);
+			
+			if (last)
+				entry_data.Add ("\0\0\0\0");
+			else
+				entry_data.Add (ByteVector.FromUInt ((uint) (data_offset + offset_data.Count), is_bigendian));
+						
+			if (data_offset - ifd_offset != entry_data.Count)
+				throw new Exception (String.Format ("Expected IFD data size was {0} but is {1}", data_offset - ifd_offset, entry_data.Count));
+				
+			entry_data.Add (offset_data);
+			
+			return entry_data;
+		}
+
+#endregion
+
+#region Protected Methods
+		
+		/// <summary>
+		///    Adds the data of a single entry to <paramref name="entry_data"/>.
+		/// </summary>
+		/// <param name="entry_data">
+		///    A <see cref="ByteVector"/> to add the entry to.
+		/// </param>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> with the type of the entry.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the data count of the entry,
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset field of the entry.
+		/// </param>
+		protected void RenderEntry (ByteVector entry_data, ushort tag, ushort type, uint count, uint offset)
+		{
+			entry_data.Add (ByteVector.FromUShort (tag, is_bigendian));
+			entry_data.Add (ByteVector.FromUShort (type, is_bigendian));
+			entry_data.Add (ByteVector.FromUInt (count, is_bigendian));
+			entry_data.Add (ByteVector.FromUInt (offset, is_bigendian));
+		}
+		
+		/// <summary>
+		///    Renders a complete entry together with the data. The entry itself
+		///    is stored in <paramref name="entry_data"/> and the data of the
+		///    entry is stored in <paramref name="offset_data"/> if it cannot be
+		///    stored in the offset. This method is called for every <see 
+		///    cref="IFDEntry"/> of this IFD and can be overwritten in subclasses
+		///    to provide special behavior.
+		/// </summary>
+		/// <param name="entry">
+		///    A <see cref="IFDEntry"/> with the entry to render.
+		/// </param>
+		/// <param name="entry_data">
+		///    A <see cref="ByteVector"/> to add the entry to.
+		/// </param>
+		/// <param name="offset_data">
+		///    A <see cref="ByteVector"/> to add the entry data to if it cannot be
+		///    stored in the offset field.
+		/// </param>
+		/// <param name="data_offset">
+		///    A <see cref="System.UInt32"/> with the offset, were the data of the
+		///    entries starts. It is needed to adjust the offsets of the entries
+		///    itself.
+		/// </param>
+		protected virtual void RenderEntryData (IFDEntry entry, ByteVector entry_data, ByteVector offset_data, uint data_offset)
+		{
+			ushort tag = (ushort) entry.Tag;
+			uint offset = (uint) (data_offset + offset_data.Count);
+			
+			ushort type;
+			uint count;
+			ByteVector data = entry.Render (is_bigendian, offset, out type, out count);
+			
+			// store data in offset, if it is smaller than 4 byte
+			if (data.Count <= 4) {
+				
+				while (data.Count < 4)
+					data.Add ("\0");
+				
+				offset = data.ToUInt (is_bigendian);
+				data = null;
+			}
+			
+			// preserve word boundary of offsets
+			if (data != null && data.Count % 2 != 0)
+				data.Add ("\0");
+			
+			RenderEntry (entry_data, tag, type, count, offset);
+			offset_data.Add (data);
+		}
+
+		/// <summary>
+		///    Constructs a new IFD Renderer used to render a <see cref="SubIFDEntry"/>.
+		/// </summary>
+		/// <param name="is_bigendian">
+		///    If IFD should be encoded in BigEndian or not.
+		/// </param>
+		/// <param name="structure">
+		///    The IFD structure that will be rendered.
+		/// </param>
+		/// <param name="ifd_offset">
+		///    A <see cref="System.UInt32"/> value with the offset of the
+		///    current IFD. All offsets inside the IFD must be adjusted
+		///    according to this given offset.
+		/// </param>
+		protected virtual IFDRenderer CreateSubRenderer (bool is_bigendian, IFDStructure structure, uint ifd_offset)
+		{
+			return new IFDRenderer (is_bigendian, structure, ifd_offset);
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/IFDStructure.cs b/lib/TagLib/TagLib/IFD/IFDStructure.cs
new file mode 100644
index 0000000..1dba617
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/IFDStructure.cs
@@ -0,0 +1,466 @@
+//
+// IFDStructure.cs: A structure resembling the logical structure of a TIFF IFD
+// file. This is the same structure as used by Exif.
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//   Paul Lange (palango gmx de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using TagLib.IFD.Entries;
+using TagLib.IFD.Tags;
+
+namespace TagLib.IFD
+{
+	/// <summary>
+	///    This class resembles the structure of a TIFF file. It can either be a
+	///    top-level IFD, or a nested IFD (in the case of Exif).
+	/// </summary>
+	public class IFDStructure
+	{
+
+#region Private Fields
+
+		private static readonly string DATETIME_FORMAT = "yyyy:MM:dd HH:mm:ss";
+
+		/// <summary>
+		///    Contains the IFD directories in this tag.
+		/// </summary>
+		internal readonly List<IFDDirectory> directories = new List<IFDDirectory> ();
+		
+#endregion
+
+#region Public Properties
+
+		/// <summary>
+		///    Gets the IFD directories contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    An array of <see cref="IFDDirectory"/> instances.
+		/// </value>
+		public IFDDirectory [] Directories {
+			get { return directories.ToArray (); }
+		}
+
+#endregion
+
+#region Public Methods
+
+		/// <summary>
+		///    Checks, if a value for the given tag is contained in the IFD.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> value with the directory index that
+		///    contains the tag.
+		/// </param>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> value with the tag.
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.Boolean"/>, which is true, if the tag is already
+		///    contained in the IFD, otherwise false.
+		/// </returns>
+		public bool ContainsTag (int directory, ushort tag)
+		{
+			if (directory >= directories.Count)
+				return false;
+			return directories [directory].ContainsKey (tag);
+		}
+
+		/// <summary>
+		///    Removes a given tag from the IFD.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> value with the directory index that
+		///    contains the tag to remove.
+		/// </param>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> value with the tag to remove.
+		/// </param>
+		public void RemoveTag (int directory, ushort tag)
+		{
+			directories [directory].Remove (tag);
+		}
+		
+		/// <summary>
+		///    Removes a given tag from the IFD.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> value with the directory index that
+		///    contains the tag to remove.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="IFDEntryTag"/> value with the tag to remove.
+		/// </param>
+		public void RemoveTag (int directory, IFDEntryTag entry_tag)
+		{
+			RemoveTag (directory, (ushort) entry_tag);
+		}
+		
+		/// <summary>
+		///    Adds an <see cref="IFDEntry"/> to the IFD, if it is not already
+		///    contained in, it fails otherwise.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> value with the directory index that
+		///    should contain the tag that will be added.
+		/// </param>
+		/// <param name="entry">
+		///    A <see cref="IFDEntry"/> to add to the IFD.
+		/// </param>
+		public void AddEntry (int directory, IFDEntry entry)
+		{
+			while (directory >= directories.Count)
+				directories.Add (new IFDDirectory ());
+
+			directories [directory].Add (entry.Tag, entry);
+		}
+		
+		/// <summary>
+		///    Adds an <see cref="IFDEntry"/> to the IFD. If it is already contained
+		///    in the IFD, it is overwritten.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> value with the directory index that
+		///    contains the tag that will be set.
+		/// </param>
+		/// <param name="entry">
+		///    A <see cref="IFDEntry"/> to add to the IFD.
+		/// </param>
+		public void SetEntry (int directory, IFDEntry entry)
+		{
+			if (ContainsTag (directory, entry.Tag))
+				RemoveTag (directory, entry.Tag);
+			
+			AddEntry (directory, entry);
+		}
+		
+		/// <summary>
+		///   Returns the <see cref="IFDEntry"/> belonging to the given tag.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the directory that contains
+		///    the wanted tag.
+		/// </param>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag to get.
+		/// </param>
+		/// <returns>
+		///    A <see cref="IFDEntry"/> belonging to the given tag, or
+		///    null, if no such tag is contained in the IFD.
+		/// </returns>
+		public IFDEntry GetEntry (int directory, ushort tag)
+		{
+			if (!ContainsTag (directory, tag))
+				return null;
+			
+			return directories [directory] [tag];
+		}
+		
+		/// <summary>
+		///   Returns the <see cref="IFDEntry"/> belonging to the given tag.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the directory that contains
+		///    the wanted tag.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="IFDEntryTag"/> with the tag to get.
+		/// </param>
+		/// <returns>
+		///    A <see cref="IFDEntry"/> belonging to the given tag, or
+		///    null, if no such tag is contained in the IFD.
+		/// </returns>
+		public IFDEntry GetEntry (int directory, IFDEntryTag entry_tag)
+		{
+			return GetEntry (directory, (ushort) entry_tag);
+		}
+		
+		/// <summary>
+		///    Returns the <see cref="System.String"/> stored in the 
+		///    entry defined by <paramref name="entry_tag"/>.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to search for the entry.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.String"/> with the value stored in the entry
+		///    or <see langword="null" /> if no such entry is contained or it
+		///    does not contain a <see cref="System.String"/> value.
+		/// </returns>
+		public string GetStringValue (int directory, ushort entry_tag)
+		{
+			var entry = GetEntry (directory, entry_tag);
+			
+			if (entry != null && entry is StringIFDEntry)
+				return (entry as StringIFDEntry).Value;
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Returns a <see cref="System.Nullable"/> containing the
+		///    <see cref="System.Byte"/> stored in the entry defined
+		///    by <paramref name="entry_tag"/>.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to search for the entry.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.Nullable"/> containing the
+		///    <see cref="System.Byte"/> stored in the entry, or
+		///    <see langword="null" /> if no such entry is contained or it
+		///    does not contain a <see cref="System.Byte"/> value.
+		/// </returns>
+		public byte? GetByteValue (int directory, ushort entry_tag)
+		{
+			var entry = GetEntry (directory, entry_tag);
+			
+			if (entry != null && entry is ByteIFDEntry)
+				return (entry as ByteIFDEntry).Value;
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Returns a <see cref="System.Nullable"/> containing the
+		///    <see cref="System.UInt32"/> stored in the entry defined
+		///    by <paramref name="entry_tag"/>.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to search for the entry.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.Nullable"/> containing the
+		///    <see cref="System.UInt32"/> stored in the entry, or
+		///    <see langword="null" /> if no such entry is contained or it
+		///    does not contain a <see cref="System.UInt32"/> value.
+		/// </returns>
+		public uint? GetLongValue (int directory, ushort entry_tag)
+		{
+			var entry = GetEntry (directory, entry_tag);
+			
+			if (entry is LongIFDEntry)
+				return (entry as LongIFDEntry).Value;
+				
+			if (entry is ShortIFDEntry)
+				return (entry as ShortIFDEntry).Value;
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Returns a <see cref="System.Nullable"/> containing the
+		///    <see cref="System.Double"/> stored in the entry defined
+		///    by <paramref name="entry_tag"/>. The entry can be of type
+		///    <see cref="Entries.RationalIFDEntry"/> or
+		///    <see cref="Entries.SRationalIFDEntry"/>
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to search for the entry.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.Nullable"/> containing the
+		///    <see cref="System.Double"/> stored in the entry, or
+		///    <see langword="null" /> if no such entry is contained.
+		/// </returns>
+		public double? GetRationalValue (int directory, ushort entry_tag)
+		{
+			var entry = GetEntry (directory, entry_tag);
+			
+			if (entry is RationalIFDEntry)
+				return (entry as RationalIFDEntry).Value;
+				
+			if (entry is SRationalIFDEntry)
+				return (entry as SRationalIFDEntry).Value;
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Returns a <see cref="System.Nullable"/> containing the
+		///    <see cref="System.DateTime"/> stored in the entry defined
+		///    by <paramref name="entry_tag"/>. The entry must be of type
+		///    <see cref="Entries.StringIFDEntry"/> and contain an datestring
+		///    according to the Exif specification.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to search for the entry.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.Nullable"/> containing the
+		///    <see cref="System.DateTime"/> stored in the entry, or
+		///    <see langword="null" /> if no such entry is contained or it
+		///    does not contain a valid value.
+		/// </returns>
+		public DateTime? GetDateTimeValue (int directory, ushort entry_tag)
+		{
+			string date_string = GetStringValue (directory, entry_tag);
+				
+			try {
+				DateTime date_time = DateTime.ParseExact (date_string,
+						DATETIME_FORMAT, System.Globalization.CultureInfo.InvariantCulture);
+				
+				return date_time;
+			} catch {}
+				
+			return null;
+		}
+		
+		/// <summary>
+		///    Adds a <see cref="Entries.StringIFDEntry"/> to the directory with tag
+		///    given by <paramref name="entry_tag"/> and value given by <paramref name="value"/>
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to add the entry to.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.String"/> with the value to add. If it is <see langword="null" />
+		///    an possibly already contained entry is removed for given tag.
+		/// </param>
+		public void SetStringValue (int directory, ushort entry_tag, string value)
+		{
+			if (value == null) {
+				RemoveTag (directory, entry_tag);
+				return;
+			}
+			
+			SetEntry (directory, new StringIFDEntry (entry_tag, value));
+		}
+
+		/// <summary>
+		///    Adds a <see cref="Entries.ByteIFDEntry"/> to the directory with tag
+		///    given by <paramref name="entry_tag"/> and value given by <paramref name="value"/>
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to add the entry to.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.Byte"/> with the value to add.
+		/// </param>
+		public void SetByteValue (int directory, ushort entry_tag, byte value)
+		{
+			SetEntry (directory, new ByteIFDEntry (entry_tag, value));
+		}
+		
+		/// <summary>
+		///    Adds a <see cref="Entries.LongIFDEntry"/> to the directory with tag
+		///    given by <paramref name="entry_tag"/> and value given by <paramref name="value"/>
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to add the entry to.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.UInt32"/> with the value to add.
+		/// </param>
+		public void SetLongValue (int directory, ushort entry_tag, uint value)
+		{
+			SetEntry (directory, new LongIFDEntry (entry_tag, value));
+		}
+		
+		/// <summary>
+		///    Adds a <see cref="Entries.RationalIFDEntry"/> to the directory with tag
+		///    given by <paramref name="entry_tag"/> and value given by <paramref name="value"/>
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to add the entry to.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.Double"/> with the value to add. It must be possible to
+		///    represent the value by a <see cref="Entries.Rational"/>.
+		/// </param>
+		public void SetRationalValue (int directory, ushort entry_tag, double value)
+		{
+			if (value < 0.0d || value > (double)UInt32.MaxValue)
+				throw new ArgumentException ("value");
+			
+			uint scale = (value >= 1.0d) ? 1 : UInt32.MaxValue;
+			
+			Rational rational = new Rational ((uint) (scale * value), scale);
+			
+			SetEntry (directory, new RationalIFDEntry (entry_tag, rational));
+		}
+		
+		/// <summary>
+		///    Adds a <see cref="Entries.StringIFDEntry"/> to the directory with tag
+		///    given by <paramref name="entry_tag"/> and value given by <paramref name="value"/>.
+		///    The value is stored as a date string according to the Exif specification.
+		/// </summary>
+		/// <param name="directory">
+		///    A <see cref="System.Int32"/> with the number of the directory
+		///    to add the entry to.
+		/// </param>
+		/// <param name="entry_tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="DateTime"/> with the value to add.
+		/// </param>
+		public void SetDateTimeValue (int directory, ushort entry_tag, DateTime value)
+		{
+			string date_string = value.ToString (DATETIME_FORMAT);
+				
+			SetStringValue (directory, entry_tag, date_string);
+		}
+
+#endregion
+
+	}
+
+}
diff --git a/lib/TagLib/TagLib/IFD/IFDTag.cs b/lib/TagLib/TagLib/IFD/IFDTag.cs
new file mode 100644
index 0000000..857a40a
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/IFDTag.cs
@@ -0,0 +1,545 @@
+//
+// IFDTag.cs: Basic Tag-class to handle an IFD (Image File Directory) with
+// its image-tags.
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//   Paul Lange (palango gmx de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using TagLib.Image;
+using TagLib.IFD.Entries;
+using TagLib.IFD.Tags;
+
+namespace TagLib.IFD
+{
+	/// <summary>
+	///    Contains the metadata for one IFD (Image File Directory).
+	/// </summary>
+	public class IFDTag : ImageTag
+	{
+
+#region Private Fields
+
+		/// <summary>
+		///    A reference to the Exif IFD (which can be found by following the
+		///    pointer in IFD0, ExifIFD tag). This variable should not be used
+		///    directly, use the <see cref="ExifIFD"/> property instead.
+		/// </summary>
+		private IFDStructure exif_ifd = null;
+
+		/// <summary>
+		///    A reference to the GPS IFD (which can be found by following the
+		///    pointer in IFD0, GPSIFD tag). This variable should not be used
+		///    directly, use the <see cref="GPSIFD"/> property instead.
+		/// </summary>
+		private IFDStructure gps_ifd = null;
+		
+#endregion
+
+#region Public Properties
+
+		/// <value>
+		///    The IFD structure referenced by the current instance
+		/// </value>
+		public IFDStructure Structure { get; private set; }
+		
+		/// <summary>
+		///    The Exif IFD. Will create one if the file doesn't alread have it.
+		/// </summary>
+		/// <remarks>
+		///    <para>Note how this also creates an empty IFD for exif, even if
+		///    you don't set a value. That's okay, empty nested IFDs get ignored
+		///    when rendering.</para>
+		/// </remarks>
+		public IFDStructure ExifIFD {
+			get {
+				if (exif_ifd == null) {
+					var entry = Structure.GetEntry (0, IFDEntryTag.ExifIFD) as SubIFDEntry;
+					if (entry == null) {
+						exif_ifd = new IFDStructure ();
+						entry = new SubIFDEntry ((ushort) IFDEntryTag.ExifIFD, (ushort) IFDEntryType.Long, 1, exif_ifd);
+						Structure.SetEntry (0, entry);
+					}
+
+					exif_ifd = entry.Structure;
+				}
+
+				return exif_ifd;
+			}
+		}
+		
+		/// <summary>
+		///    The GPS IFD. Will create one if the file doesn't alread have it.
+		/// </summary>
+		/// <remarks>
+		///    <para>Note how this also creates an empty IFD for GPS, even if
+		///    you don't set a value. That's okay, empty nested IFDs get ignored
+		///    when rendering.</para>
+		/// </remarks>
+		public IFDStructure GPSIFD {
+			get {
+				if (gps_ifd == null) {
+					var entry = Structure.GetEntry (0, IFDEntryTag.GPSIFD) as SubIFDEntry;
+					if (entry == null) {
+						gps_ifd = new IFDStructure ();
+						entry = new SubIFDEntry ((ushort) IFDEntryTag.GPSIFD, (ushort) IFDEntryType.Long, 1, gps_ifd);
+						Structure.SetEntry (0, entry);
+					}
+
+					gps_ifd = entry.Structure;
+				}
+
+				return gps_ifd;
+			}
+		}
+
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.TiffIFD" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get { return TagTypes.TiffIFD; }
+		}
+		
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Constructor. Creates an empty IFD tag. Can be populated manually, or via 
+		///    <see cref="IFDReader"/>.
+		/// </summary>
+		public IFDTag ()
+		{
+			Structure = new IFDStructure ();
+		}
+
+#endregion
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			throw new NotImplementedException ();
+		}
+		
+#endregion
+
+#region Metadata fields
+
+		/// <summary>
+		///    Gets or sets the comment for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the comment of the
+		///    current instace.
+		/// </value>
+		public override string Comment {
+			get {
+				var comment_entry = ExifIFD.GetEntry (0, (ushort) ExifEntryTag.UserComment) as UserCommentIFDEntry;
+				
+				if (comment_entry == null) {
+					var description = Structure.GetEntry (0, IFDEntryTag.ImageDescription) as StringIFDEntry;
+					return description == null ? null : description.Value;
+				}
+
+				return comment_entry.Value;
+			}
+			set {
+				if (value == null) {
+					ExifIFD.RemoveTag (0, (ushort) ExifEntryTag.UserComment);
+					Structure.RemoveTag (0, (ushort) IFDEntryTag.ImageDescription);
+				}
+				
+				ExifIFD.SetEntry (0, new UserCommentIFDEntry ((ushort) ExifEntryTag.UserComment, value));
+				Structure.SetEntry (0, new StringIFDEntry ((ushort) IFDEntryTag.ImageDescription, value));
+			}
+		}
+
+		/// <summary>
+		///    Gets or sets the time when the image, the current instance
+		///    belongs to, was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the time the image was taken.
+		/// </value>
+		public override DateTime? DateTime {
+			get { return DateTimeOriginal; }
+			set { DateTimeOriginal = value; }
+		}
+
+		/// <summary>
+		///    The time of capturing.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the time of capturing.
+		/// </value>
+		public DateTime? DateTimeOriginal {
+			get {
+				return ExifIFD.GetDateTimeValue (0, (ushort) ExifEntryTag.DateTimeOriginal);
+			}
+			set {
+				if (value == null) {
+					ExifIFD.RemoveTag (0, (ushort) ExifEntryTag.DateTimeOriginal);
+					return;
+				}
+				
+				ExifIFD.SetDateTimeValue (0, (ushort) ExifEntryTag.DateTimeOriginal, value.Value);
+			}
+		}
+
+		/// <summary>
+		///    The time of digitization.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the time of digitization.
+		/// </value>
+		public DateTime? DateTimeDigitized {
+			get {
+				return ExifIFD.GetDateTimeValue (0, (ushort) ExifEntryTag.DateTimeDigitized);
+			}
+			set {
+				if (value == null) {
+					ExifIFD.RemoveTag (0, (ushort) ExifEntryTag.DateTimeDigitized);
+					return;
+				}
+				
+				ExifIFD.SetDateTimeValue (0, (ushort) ExifEntryTag.DateTimeDigitized, value.Value);
+			}
+		}
+
+		/// <summary>
+		///    Gets or sets the latitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the latitude ranging from -90.0
+		///    to +90.0 degrees.
+		/// </value>
+		public override double? Latitude {
+			get {
+				var gps_ifd = GPSIFD;
+				var degree_entry = gps_ifd.GetEntry (0, (ushort) GPSEntryTag.GPSLatitude) as RationalArrayIFDEntry;
+				var degree_ref = gps_ifd.GetStringValue (0, (ushort) GPSEntryTag.GPSLatitudeRef);
+				
+				if (degree_entry == null || degree_ref == null)
+					return null;
+				
+				Rational [] values  = degree_entry.Values;
+				if (values.Length != 3)
+					return null;
+				
+				double deg = values[0] + values[1] / 60.0d + values[2] / 3600.0d;
+				
+				if (degree_ref == "S")
+					deg *= -1.0d;
+				
+				return Math.Max (Math.Min (deg, 90.0d), -90.0d);
+			}
+			set {
+				var gps_ifd = GPSIFD;
+				
+				if (value == null) {
+					gps_ifd.RemoveTag (0, (ushort) GPSEntryTag.GPSLatitudeRef);
+					gps_ifd.RemoveTag (0, (ushort) GPSEntryTag.GPSLatitude);
+					return;
+				}
+				
+				double angle = value.Value;
+				
+				if (angle < -90.0d || angle > 90.0d)
+					throw new ArgumentException ("value");
+				
+				InitGpsDirectory ();
+				
+				gps_ifd.SetStringValue (0, (ushort) GPSEntryTag.GPSLatitudeRef, angle < 0 ? "S" : "N");
+				
+				var entry =
+					new RationalArrayIFDEntry ((ushort) GPSEntryTag.GPSLatitude,
+					                           DegreeToRationals (Math.Abs (angle)));
+				gps_ifd.SetEntry (0, entry);
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the longitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the longitude ranging from -180.0
+		///    to +180.0 degrees.
+		/// </value>
+		public override double? Longitude {
+			get {
+				var gps_ifd = GPSIFD;
+				var degree_entry = gps_ifd.GetEntry (0, (ushort) GPSEntryTag.GPSLongitude) as RationalArrayIFDEntry;
+				var degree_ref = gps_ifd.GetStringValue (0, (ushort) GPSEntryTag.GPSLongitudeRef);
+				
+				if (degree_entry == null || degree_ref == null)
+					return null;
+				
+				Rational [] values  = degree_entry.Values;
+				if (values.Length != 3)
+					return null;
+				
+				double deg = values[0] + values[1] / 60.0d + values[2] / 3600.0d;
+				
+				if (degree_ref == "W")
+					deg *= -1.0d;
+				
+				return Math.Max (Math.Min (deg, 180.0d), -180.0d);
+			}
+			set {
+				var gps_ifd = GPSIFD;
+				
+				if (value == null) {
+					gps_ifd.RemoveTag (0, (ushort) GPSEntryTag.GPSLongitudeRef);
+					gps_ifd.RemoveTag (0, (ushort) GPSEntryTag.GPSLongitude);
+					return;
+				}
+				
+				double angle = value.Value;
+				
+				if (angle < -180.0d || angle > 180.0d)
+					throw new ArgumentException ("value");
+				
+				InitGpsDirectory ();
+				
+				gps_ifd.SetStringValue (0, (ushort) GPSEntryTag.GPSLongitudeRef, angle < 0 ? "W" : "E");
+				
+				var entry =
+					new RationalArrayIFDEntry ((ushort) GPSEntryTag.GPSLongitude,
+					                           DegreeToRationals (Math.Abs (angle)));
+				gps_ifd.SetEntry (0, entry);
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the altitude of the GPS coordinate the current
+		///    image was taken. The unit is meter.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the altitude. A positive value
+		///    is above sea level, a negative one below sea level. The unit is meter.
+		/// </value>
+		public override double? Altitude {
+			get {
+				var gps_ifd = GPSIFD;
+				var altitude = gps_ifd.GetRationalValue (0, (ushort) GPSEntryTag.GPSAltitude);
+				var ref_entry = gps_ifd.GetByteValue (0, (ushort) GPSEntryTag.GPSAltitudeRef);
+				
+				if (altitude == null || ref_entry == null)
+					return null;
+
+				if (ref_entry.Value == 1)
+					altitude *= -1.0d;
+				
+				return altitude;
+			}
+			set {
+				var gps_ifd = GPSIFD;
+				
+				if (value == null) {
+					gps_ifd.RemoveTag (0, (ushort) GPSEntryTag.GPSAltitudeRef);
+					gps_ifd.RemoveTag (0, (ushort) GPSEntryTag.GPSAltitude);
+					return;
+				}
+				
+				double altitude = value.Value;
+				
+				InitGpsDirectory ();
+				
+				gps_ifd.SetByteValue (0, (ushort) GPSEntryTag.GPSAltitudeRef, (byte)(altitude < 0 ? 1 : 0));
+				gps_ifd.SetRationalValue (0, (ushort) GPSEntryTag.GPSAltitude, Math.Abs (altitude));
+			}
+		}
+		
+		/// <summary>
+		///    Gets the exposure time the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the exposure time in seconds.
+		/// </value>
+		public override double? ExposureTime {
+			get {
+				return ExifIFD.GetRationalValue (0, (ushort) ExifEntryTag.ExposureTime);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the FNumber the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the FNumber.
+		/// </value>
+		public override double? FNumber {
+			get {
+				return ExifIFD.GetRationalValue (0, (ushort) ExifEntryTag.FNumber);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the ISO speed the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the ISO speed as defined in ISO 12232.
+		/// </value>
+		public override uint? ISOSpeedRatings {
+			get {
+				return ExifIFD.GetLongValue (0, (ushort) ExifEntryTag.ISOSpeedRatings);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the focal length the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the focal length in millimeters.
+		/// </value>
+		public override double? FocalLength {
+			get {
+				return ExifIFD.GetRationalValue (0, (ushort) ExifEntryTag.FocalLength);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the focal length the image, the current instance belongs
+		///    to, was taken with, assuming a 35mm film camera.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the focal length in 35mm equivalent in millimeters.
+		/// </value>
+		public override uint? FocalLengthIn35mmFilm {
+			get {
+				return ExifIFD.GetLongValue (0, (ushort) ExifEntryTag.FocalLengthIn35mmFilm);
+			}
+		}
+
+		/// <summary>
+		///    Gets or sets the orientation of the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Image.ImageOrientation" /> containing the orientation of the
+		///    image
+		/// </value>
+		public override ImageOrientation Orientation {
+			get {
+				return (ImageOrientation) (Structure.GetLongValue (0, (ushort) IFDEntryTag.Orientation) ?? 1);
+			}
+			set {
+				if ((uint) value < 1U || (uint) value > 8U) {
+					Structure.RemoveTag (0, (ushort) IFDEntryTag.Orientation);
+					return;
+				}
+				
+				Structure.SetLongValue (0, (ushort) IFDEntryTag.Orientation, (uint) value);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the manufacture of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the manufacture name.
+		/// </value>
+		public override string Make {
+			get {
+				return Structure.GetStringValue (0, (ushort) IFDEntryTag.Make);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the model name of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the model name.
+		/// </value>
+		public override string Model {
+			get {
+				return Structure.GetStringValue (0, (ushort) IFDEntryTag.Model);
+			}
+		}
+
+#endregion
+
+#region Private Methods
+		
+		/// <summary>
+		///    Initilazies the GPS IFD with some basic entries.
+		/// </summary>
+		private void InitGpsDirectory ()
+		{
+			GPSIFD.SetStringValue (0, (ushort) GPSEntryTag.GPSVersionID, "2 0 0 0");
+			GPSIFD.SetStringValue (0, (ushort) GPSEntryTag.GPSMapDatum, "WGS-84");
+		}
+		
+		/// <summary>
+		///    Converts a given (positive) angle value to three rationals like they
+		///    are used to store an angle for GPS data.
+		/// </summary>
+		/// <param name="angle">
+		///    A <see cref="System.Double"/> between 0.0d and 180.0d with the angle
+		///    in degrees
+		/// </param>
+		/// <returns>
+		///    A <see cref="Rational"/> representing the same angle by degree, minutes
+		///    and seconds of the angle.
+		/// </returns>
+		private Rational[] DegreeToRationals (double angle)
+		{
+			if (angle < 0.0 || angle > 180.0)
+				throw new ArgumentException ("angle");
+			
+			uint deg = (uint) Math.Floor (angle);
+			uint min = (uint) ((angle - Math.Floor (angle)) * 60.0);
+			uint sec = (uint) ((angle - Math.Floor (angle) - (min / 60.0))  * 360000000.0);
+				
+			Rational[] rationals = new Rational [] {
+				new Rational (deg, 1),
+				new Rational (min, 1),
+				new Rational (sec, 100000)
+			};
+				
+			return rationals;
+		}
+		
+#endregion
+		
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Makernotes/Nikon3MakernoteReader.cs b/lib/TagLib/TagLib/IFD/Makernotes/Nikon3MakernoteReader.cs
new file mode 100644
index 0000000..d1e9654
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Makernotes/Nikon3MakernoteReader.cs
@@ -0,0 +1,123 @@
+//
+// Nikon3MakernoteReader.cs: Reads Nikon Makernotes.
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using TagLib.IFD.Entries;
+using TagLib.IFD.Tags;
+
+namespace TagLib.IFD.Makernotes
+{
+	/// <summary>
+	///     This class contains Nikon3 makernote specific reading logic.
+	/// </summary>
+	public class Nikon3MakernoteReader : IFDReader {
+#region Constructors
+
+		/// <summary>
+		///    Constructor. Reads an IFD from given file, using the given endianness.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File"/> to read from.
+		/// </param>
+		/// <param name="is_bigendian">
+		///     A <see cref="System.Boolean"/>, it must be true, if the data of the IFD should be
+		///     read as bigendian, otherwise false.
+		/// </param>
+		/// <param name="structure">
+		///    A <see cref="IFDStructure"/> that will be populated.
+		/// </param>
+		/// <param name="base_offset">
+		///     A <see cref="System.Int64"/> value describing the base were the IFD offsets
+		///     refer to. E.g. in Jpegs the IFD are located in an Segment and the offsets
+		///     inside the IFD refer from the beginning of this segment. So <paramref
+		///     name="base_offset"/> must contain the beginning of the segment.
+		/// </param>
+		/// <param name="ifd_offset">
+		///     A <see cref="System.UInt32"/> value with the beginning of the IFD relative to 
+		///     <paramref name="base_offset"/>.
+		/// </param>
+		/// <param name="max_offset">
+		/// 	A <see cref="System.UInt32"/> value with maximal possible offset. This is to limit
+		///     the size of the possible data;
+		/// </param>
+		public Nikon3MakernoteReader (File file, bool is_bigendian, IFDStructure structure, long base_offset, uint ifd_offset, uint max_offset) :
+			base (file, is_bigendian, structure, base_offset, ifd_offset, max_offset)
+		{
+		}
+
+#endregion
+
+#region Protected Methods
+
+		/// <summary>
+		///    Try to parse the given IFD entry, used to discover format-specific entries.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="System.UInt16"/> with the tag of the entry.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="System.UInt16"/> with the type of the entry.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="System.UInt32"/> with the data count of the entry.
+		/// </param>
+		/// <param name="base_offset">
+		///    A <see cref="System.Int64"/> with the base offset which every offsets in the
+		///    IFD are relative to.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="System.UInt32"/> with the offset of the entry.
+		/// </param>
+		/// <returns>
+		///    A <see cref="IFDEntry"/> with the given parameters, or null if none was parsed, after
+		///    which the normal TIFF parsing is used.
+		/// </returns>
+		protected override IFDEntry ParseIFDEntry (ushort tag, ushort type, uint count, long base_offset, uint offset)
+		{
+			if (tag == (ushort) Nikon3MakerNoteEntryTag.Preview) {
+				// SubIFD with Preview Image
+				// The entry itself is usually a long
+				// TODO: handle JPEGInterchangeFormat and JPEGInterchangeFormatLength correctly
+				
+				// The preview field contains a long with an offset to an IFD
+				// that contains the preview image. We need to be careful
+				// though: this IFD does not contain a valid next-offset
+				// pointer. For this reason, we only read the first IFD and
+				// ignore the rest (which is preview image data, directly
+				// starting after the IFD entries).
+
+				type = (ushort) IFDEntryType.IFD;
+				
+				IFDStructure ifd_structure = new IFDStructure ();
+				IFDReader reader = CreateSubIFDReader (file, is_bigendian, ifd_structure, base_offset, offset, max_offset);
+			
+				reader.Read (1);
+				return new SubIFDEntry (tag, type, (uint) ifd_structure.Directories.Length, ifd_structure);
+			}
+			return base.ParseIFDEntry (tag, type, count, base_offset, offset);
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/CanonMakerNoteEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/CanonMakerNoteEntryTag.cs
new file mode 100644
index 0000000..f4bb08c
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/CanonMakerNoteEntryTag.cs
@@ -0,0 +1,159 @@
+//
+// CanonMakerNoteEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009-2010 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Label tags for Canon Makernote.
+	///    Based on http://www.burren.cx/david/canon.html and http://www.exiv2.org/tags-canon.html
+	/// </summary>
+	public enum CanonMakerNoteEntryTag : ushort
+	{
+		/// <summary>
+		///    Unknown field at tag 0x0000. (Hex: 0x0000)
+		/// </summary>
+		Unknown0                                           = 0,
+
+		/// <summary>
+		///    Camera Settings. (Hex: 0x0001)
+		/// </summary>
+		CameraSettings                                      = 1,
+
+		/// <summary>
+		///    Focal Length. (Hex: 0x0002)
+		/// </summary>
+		FocalLength                                         = 2,
+
+		/// <summary>
+		///    Unknown field at tag 0x0000. (Hex: 0x0003)
+		/// </summary>
+		Unknown3                                            = 3,
+
+		/// <summary>
+		///    Shot Information. (Hex: 0x0004)
+		/// </summary>
+		ShotInfo                                            = 4,
+
+		/// <summary>
+		///    Panorama. (Hex: 0x0005)
+		/// </summary>
+		Panorama                                            = 5,
+
+		/// <summary>
+		///    Image Type. (Hex: 0x0006)
+		/// </summary>
+		ImageType                                           = 6,
+
+		/// <summary>
+		///    Firmware Version. (Hex: 0x0007)
+		/// </summary>
+		FirmwareVersion                                     = 7,
+
+		/// <summary>
+		///    Image Number. (Hex: 0x0008)
+		/// </summary>
+		ImageNumber                                         = 8,
+
+		/// <summary>
+		///    Owner Name. (Hex: 0x0009)
+		/// </summary>
+		OwnerName                                           = 9,
+
+		/// <summary>
+		///    Serial Number. (Hex: 0x000C)
+		/// </summary>
+		SerialNumber                                        = 12,
+
+		/// <summary>
+		///    Unknown field at tag 0x0000. (Hex: 0x000D)
+		/// </summary>
+		Unknown13                                           = 13,
+
+		/// <summary>
+		///    Custom Functions. (Hex: 0x000F)
+		/// </summary>
+		CustomFunctions                                     = 15,
+
+		/// <summary>
+		///    Model ID. (Hex: 0x0010)
+		/// </summary>
+		ModelID                                             = 16,
+
+		/// <summary>
+		///    Picture Info. (Hex: 0x0012)
+		/// </summary>
+		PictureInfo                                         = 18,
+
+		/// <summary>
+		///    Serial Number Format. (Hex: 0x0015)
+		/// </summary>
+		SerialNumberFormat                                  = 21,
+
+		/// <summary>
+		///    Canon File Info. (Hex: 0x0093)
+		/// </summary>
+		CanonFileInfo                                       = 147,
+
+		/// <summary>
+		///    Lens Model. (Hex: 0x0095)
+		/// </summary>
+		LensModel                                           = 149,
+
+		/// <summary>
+		///    Serial Info. (Hex: 0x0096)
+		/// </summary>
+		SerialInfo                                          = 150,
+
+		/// <summary>
+		///    Processing Info. (Hex: 0x00A0)
+		/// </summary>
+		ProcessingInfo                                      = 160,
+
+		/// <summary>
+		///    White Balance Table. (Hex: 0x00A9)
+		/// </summary>
+		WhiteBalanceTable                                   = 169,
+
+		/// <summary>
+		///    Measured Color. (Hex: 0x00AA)
+		/// </summary>
+		MeasuredColor                                       = 170,
+
+		/// <summary>
+		///    Color Space. (Hex: 0x00B4)
+		/// </summary>
+		ColorSpace                                          = 180,
+
+		/// <summary>
+		///    Sensor Info. (Hex: 0x00E0)
+		/// </summary>
+		SensorInfo                                          = 224,
+
+		/// <summary>
+		///    Black Level. (Hex: 0x4008)
+		/// </summary>
+		BlackLevel                                          = 16392,
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/ExifEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/ExifEntryTag.cs
new file mode 100644
index 0000000..fbd9816
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/ExifEntryTag.cs
@@ -0,0 +1,372 @@
+//
+// ExifEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009-2010 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Entry tags occuring in the Exif IFD
+	///    The complete overview can be obtained at:
+	///    http://www.awaresystems.be/imaging/tiff.html
+	/// </summary>
+	public enum ExifEntryTag : ushort
+	{
+		
+		/// <summary>
+		///     Exposure time, given in seconds. (Hex: 0x829A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/exposuretime.html
+		/// </summary>
+		ExposureTime                                       = 33434,
+		
+		/// <summary>
+		///     The F number. (Hex: 0x829D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/fnumber.html
+		/// </summary>
+		FNumber                                            = 33437,
+		
+		/// <summary>
+		///     The class of the program used by the camera to set exposure when the picture is taken. (Hex: 0x8822)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/exposureprogram.html
+		/// </summary>
+		ExposureProgram                                    = 34850,
+		
+		/// <summary>
+		///     Indicates the spectral sensitivity of each channel of the camera used. (Hex: 0x8824)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/spectralsensitivity.html
+		/// </summary>
+		SpectralSensitivity                                = 34852,
+		
+		/// <summary>
+		///     Indicates the ISO Speed and ISO Latitude of the camera or input device as specified in ISO 12232. (Hex: 0x8827)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/isospeedratings.html
+		/// </summary>
+		ISOSpeedRatings                                    = 34855,
+		
+		/// <summary>
+		///     Indicates the Opto-Electric Conversion Function (OECF) specified in ISO 14524. (Hex: 0x8828)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/oecf.html
+		/// </summary>
+		OECF                                               = 34856,
+		
+		/// <summary>
+		///     The version of the supported Exif standard. (Hex: 0x9000)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/exifversion.html
+		/// </summary>
+		ExifVersion                                        = 36864,
+		
+		/// <summary>
+		///     The date and time when the original image data was generated. (Hex: 0x9003)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimeoriginal.html
+		/// </summary>
+		DateTimeOriginal                                   = 36867,
+		
+		/// <summary>
+		///     The date and time when the image was stored as digital data. (Hex: 0x9004)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimedigitized.html
+		/// </summary>
+		DateTimeDigitized                                  = 36868,
+		
+		/// <summary>
+		///     Specific to compressed data; specifies the channels and complements PhotometricInterpretation (Hex: 0x9101)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/componentsconfiguration.html
+		/// </summary>
+		ComponentsConfiguration                            = 37121,
+		
+		/// <summary>
+		///     Specific to compressed data; states the compressed bits per pixel. (Hex: 0x9102)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/compressedbitsperpixel.html
+		/// </summary>
+		CompressedBitsPerPixel                             = 37122,
+		
+		/// <summary>
+		///     Shutter speed. (Hex: 0x9201)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/shutterspeedvalue.html
+		/// </summary>
+		ShutterSpeedValue                                  = 37377,
+		
+		/// <summary>
+		///     The lens aperture. (Hex: 0x9202)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/aperturevalue.html
+		/// </summary>
+		ApertureValue                                      = 37378,
+		
+		/// <summary>
+		///     The value of brightness. (Hex: 0x9203)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/brightnessvalue.html
+		/// </summary>
+		BrightnessValue                                    = 37379,
+		
+		/// <summary>
+		///     The exposure bias. (Hex: 0x9204)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/exposurebiasvalue.html
+		/// </summary>
+		ExposureBiasValue                                  = 37380,
+		
+		/// <summary>
+		///     The smallest F number of the lens. (Hex: 0x9205)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/maxaperturevalue.html
+		/// </summary>
+		MaxApertureValue                                   = 37381,
+		
+		/// <summary>
+		///     The distance to the subject, given in meters. (Hex: 0x9206)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/subjectdistance.html
+		/// </summary>
+		SubjectDistance                                    = 37382,
+		
+		/// <summary>
+		///     The metering mode. (Hex: 0x9207)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/meteringmode.html
+		/// </summary>
+		MeteringMode                                       = 37383,
+		
+		/// <summary>
+		///     The kind of light source. (Hex: 0x9208)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/lightsource.html
+		/// </summary>
+		LightSource                                        = 37384,
+		
+		/// <summary>
+		///     Indicates the status of flash when the image was shot. (Hex: 0x9209)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/flash.html
+		/// </summary>
+		Flash                                              = 37385,
+		
+		/// <summary>
+		///     The actual focal length of the lens, in mm. (Hex: 0x920A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/focallength.html
+		/// </summary>
+		FocalLength                                        = 37386,
+		
+		/// <summary>
+		///     Indicates the location and area of the main subject in the overall scene. (Hex: 0x9214)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/subjectarea.html
+		/// </summary>
+		SubjectArea                                        = 37396,
+		
+		/// <summary>
+		///     Manufacturer specific information. (Hex: 0x927C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/makernote.html
+		/// </summary>
+		MakerNote                                          = 37500,
+		
+		/// <summary>
+		///     Keywords or comments on the image; complements ImageDescription. (Hex: 0x9286)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/usercomment.html
+		/// </summary>
+		UserComment                                        = 37510,
+		
+		/// <summary>
+		///     A tag used to record fractions of seconds for the DateTime tag. (Hex: 0x9290)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/subsectime.html
+		/// </summary>
+		SubsecTime                                         = 37520,
+		
+		/// <summary>
+		///     A tag used to record fractions of seconds for the DateTimeOriginal tag. (Hex: 0x9291)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/subsectimeoriginal.html
+		/// </summary>
+		SubsecTimeOriginal                                 = 37521,
+		
+		/// <summary>
+		///     A tag used to record fractions of seconds for the DateTimeDigitized tag. (Hex: 0x9292)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/subsectimedigitized.html
+		/// </summary>
+		SubsecTimeDigitized                                = 37522,
+		
+		/// <summary>
+		///     The Flashpix format version supported by a FPXR file. (Hex: 0xA000)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/flashpixversion.html
+		/// </summary>
+		FlashpixVersion                                    = 40960,
+		
+		/// <summary>
+		///     The color space information tag is always recorded as the color space specifier. (Hex: 0xA001)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/colorspace.html
+		/// </summary>
+		ColorSpace                                         = 40961,
+		
+		/// <summary>
+		///     Specific to compressed data; the valid width of the meaningful image. (Hex: 0xA002)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/pixelxdimension.html
+		/// </summary>
+		PixelXDimension                                    = 40962,
+		
+		/// <summary>
+		///     Specific to compressed data; the valid height of the meaningful image. (Hex: 0xA003)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/pixelydimension.html
+		/// </summary>
+		PixelYDimension                                    = 40963,
+		
+		/// <summary>
+		///     Used to record the name of an audio file related to the image data. (Hex: 0xA004)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/relatedsoundfile.html
+		/// </summary>
+		RelatedSoundFile                                   = 40964,
+		
+		/// <summary>
+		///     Indicates the strobe energy at the time the image is captured, as measured in Beam Candle Power Seconds (Hex: 0xA20B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/flashenergy.html
+		/// </summary>
+		FlashEnergy                                        = 41483,
+		
+		/// <summary>
+		///     Records the camera or input device spatial frequency table and SFR values in the direction of image width, image height, and diagonal direction, as specified in ISO 12233. (Hex: 0xA20C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/spatialfrequencyresponse.html
+		/// </summary>
+		SpatialFrequencyResponse                           = 41484,
+		
+		/// <summary>
+		///     Indicates the number of pixels in the image width (X) direction per FocalPlaneResolutionUnit on the camera focal plane. (Hex: 0xA20E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/focalplanexresolution.html
+		/// </summary>
+		FocalPlaneXResolution                              = 41486,
+		
+		/// <summary>
+		///     Indicates the number of pixels in the image height (Y) direction per FocalPlaneResolutionUnit on the camera focal plane. (Hex: 0xA20F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/focalplaneyresolution.html
+		/// </summary>
+		FocalPlaneYResolution                              = 41487,
+		
+		/// <summary>
+		///     Indicates the unit for measuring FocalPlaneXResolution and FocalPlaneYResolution. (Hex: 0xA210)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/focalplaneresolutionunit.html
+		/// </summary>
+		FocalPlaneResolutionUnit                           = 41488,
+		
+		/// <summary>
+		///     Indicates the location of the main subject in the scene. (Hex: 0xA214)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/subjectlocation.html
+		/// </summary>
+		SubjectLocation                                    = 41492,
+		
+		/// <summary>
+		///     Indicates the exposure index selected on the camera or input device at the time the image is captured. (Hex: 0xA215)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/exposureindex.html
+		/// </summary>
+		ExposureIndex                                      = 41493,
+		
+		/// <summary>
+		///     Indicates the image sensor type on the camera or input device. (Hex: 0xA217)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/sensingmethod.html
+		/// </summary>
+		SensingMethod                                      = 41495,
+		
+		/// <summary>
+		///     Indicates the image source. (Hex: 0xA300)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/filesource.html
+		/// </summary>
+		FileSource                                         = 41728,
+		
+		/// <summary>
+		///     Indicates the type of scene. (Hex: 0xA301)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/scenetype.html
+		/// </summary>
+		SceneType                                          = 41729,
+		
+		/// <summary>
+		///     Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. (Hex: 0xA302)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/cfapattern.html
+		/// </summary>
+		CFAPattern                                         = 41730,
+		
+		/// <summary>
+		///     Indicates the use of special processing on image data, such as rendering geared to output. (Hex: 0xA401)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/customrendered.html
+		/// </summary>
+		CustomRendered                                     = 41985,
+		
+		/// <summary>
+		///     Indicates the exposure mode set when the image was shot. (Hex: 0xA402)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/exposuremode.html
+		/// </summary>
+		ExposureMode                                       = 41986,
+		
+		/// <summary>
+		///     Indicates the white balance mode set when the image was shot. (Hex: 0xA403)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/whitebalance.html
+		/// </summary>
+		WhiteBalance                                       = 41987,
+		
+		/// <summary>
+		///     Indicates the digital zoom ratio when the image was shot. (Hex: 0xA404)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/digitalzoomratio.html
+		/// </summary>
+		DigitalZoomRatio                                   = 41988,
+		
+		/// <summary>
+		///     Indicates the equivalent focal length assuming a 35mm film camera, in mm. (Hex: 0xA405)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/focallengthin35mmfilm.html
+		/// </summary>
+		FocalLengthIn35mmFilm                              = 41989,
+		
+		/// <summary>
+		///     Indicates the type of scene that was shot. (Hex: 0xA406)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/scenecapturetype.html
+		/// </summary>
+		SceneCaptureType                                   = 41990,
+		
+		/// <summary>
+		///     Indicates the degree of overall image gain adjustment. (Hex: 0xA407)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/gaincontrol.html
+		/// </summary>
+		GainControl                                        = 41991,
+		
+		/// <summary>
+		///     Indicates the direction of contrast processing applied by the camera when the image was shot. (Hex: 0xA408)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/contrast.html
+		/// </summary>
+		Contrast                                           = 41992,
+		
+		/// <summary>
+		///     Indicates the direction of saturation processing applied by the camera when the image was shot. (Hex: 0xA409)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/saturation.html
+		/// </summary>
+		Saturation                                         = 41993,
+		
+		/// <summary>
+		///     Indicates the direction of sharpness processing applied by the camera when the image was shot. (Hex: 0xA40A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/sharpness.html
+		/// </summary>
+		Sharpness                                          = 41994,
+		
+		/// <summary>
+		///     This tag indicates information on the picture-taking conditions of a particular camera model. (Hex: 0xA40B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/devicesettingdescription.html
+		/// </summary>
+		DeviceSettingDescription                           = 41995,
+		
+		/// <summary>
+		///     Indicates the distance to the subject. (Hex: 0xA40C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/subjectdistancerange.html
+		/// </summary>
+		SubjectDistanceRange                               = 41996,
+		
+		/// <summary>
+		///     Indicates an identifier assigned uniquely to each image. (Hex: 0xA420)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/imageuniqueid.html
+		/// </summary>
+		ImageUniqueID                                      = 42016,
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/GPSEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/GPSEntryTag.cs
new file mode 100644
index 0000000..8eabe90
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/GPSEntryTag.cs
@@ -0,0 +1,222 @@
+//
+// GPSEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009-2010 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Entry tags occuring in the GPS IFD
+	///    The complete overview can be obtained at:
+	///    http://www.awaresystems.be/imaging/tiff.html
+	/// </summary>
+	public enum GPSEntryTag : ushort
+	{
+		
+		/// <summary>
+		///     Indicates the version of GPSInfoIFD. (Hex: 0x0000)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsversionid.html
+		/// </summary>
+		GPSVersionID                                       = 0,
+		
+		/// <summary>
+		///     Indicates whether the latitude is north or south latitude. (Hex: 0x0001)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpslatituderef.html
+		/// </summary>
+		GPSLatitudeRef                                     = 1,
+		
+		/// <summary>
+		///     Indicates the latitude. (Hex: 0x0002)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpslatitude.html
+		/// </summary>
+		GPSLatitude                                        = 2,
+		
+		/// <summary>
+		///     Indicates whether the longitude is east or west longitude. (Hex: 0x0003)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpslongituderef.html
+		/// </summary>
+		GPSLongitudeRef                                    = 3,
+		
+		/// <summary>
+		///     Indicates the longitude. (Hex: 0x0004)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpslongitude.html
+		/// </summary>
+		GPSLongitude                                       = 4,
+		
+		/// <summary>
+		///     Indicates the altitude used as the reference altitude. (Hex: 0x0005)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsaltituderef.html
+		/// </summary>
+		GPSAltitudeRef                                     = 5,
+		
+		/// <summary>
+		///     Indicates the altitude based on the reference in GPSAltitudeRef. (Hex: 0x0006)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsaltitude.html
+		/// </summary>
+		GPSAltitude                                        = 6,
+		
+		/// <summary>
+		///     Indicates the time as UTC (Coordinated Universal Time). (Hex: 0x0007)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpstimestamp.html
+		/// </summary>
+		GPSTimeStamp                                       = 7,
+		
+		/// <summary>
+		///     Indicates the GPS satellites used for measurements. (Hex: 0x0008)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpssatellites.html
+		/// </summary>
+		GPSSatellites                                      = 8,
+		
+		/// <summary>
+		///     Indicates the status of the GPS receiver when the image is recorded. (Hex: 0x0009)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsstatus.html
+		/// </summary>
+		GPSStatus                                          = 9,
+		
+		/// <summary>
+		///     Indicates the GPS measurement mode. (Hex: 0x000A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsmeasuremode.html
+		/// </summary>
+		GPSMeasureMode                                     = 10,
+		
+		/// <summary>
+		///     Indicates the GPS DOP (data degree of precision). (Hex: 0x000B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdop.html
+		/// </summary>
+		GPSDOP                                             = 11,
+		
+		/// <summary>
+		///     Indicates the unit used to express the GPS receiver speed of movement. (Hex: 0x000C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsspeedref.html
+		/// </summary>
+		GPSSpeedRef                                        = 12,
+		
+		/// <summary>
+		///     Indicates the speed of GPS receiver movement. (Hex: 0x000D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsspeed.html
+		/// </summary>
+		GPSSpeed                                           = 13,
+		
+		/// <summary>
+		///     Indicates the reference for giving the direction of GPS receiver movement. (Hex: 0x000E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpstrackref.html
+		/// </summary>
+		GPSTrackRef                                        = 14,
+		
+		/// <summary>
+		///     Indicates the direction of GPS receiver movement. (Hex: 0x000F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpstrack.html
+		/// </summary>
+		GPSTrack                                           = 15,
+		
+		/// <summary>
+		///     Indicates the reference for giving the direction of the image when it is captured. (Hex: 0x0010)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsimgdirectionref.html
+		/// </summary>
+		GPSImgDirectionRef                                 = 16,
+		
+		/// <summary>
+		///     Indicates the direction of the image when it was captured. (Hex: 0x0011)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsimgdirection.html
+		/// </summary>
+		GPSImgDirection                                    = 17,
+		
+		/// <summary>
+		///     Indicates the geodetic survey data used by the GPS receiver. (Hex: 0x0012)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsmapdatum.html
+		/// </summary>
+		GPSMapDatum                                        = 18,
+		
+		/// <summary>
+		///     Indicates whether the latitude of the destination point is north or south latitude. (Hex: 0x0013)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestlatituderef.html
+		/// </summary>
+		GPSDestLatitudeRef                                 = 19,
+		
+		/// <summary>
+		///     Indicates the latitude of the destination point. (Hex: 0x0014)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestlatitude.html
+		/// </summary>
+		GPSDestLatitude                                    = 20,
+		
+		/// <summary>
+		///     Indicates whether the longitude of the destination point is east or west longitude. (Hex: 0x0015)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestlongituderef.html
+		/// </summary>
+		GPSDestLongitudeRef                                = 21,
+		
+		/// <summary>
+		///     Indicates the longitude of the destination point. (Hex: 0x0016)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestlongitude.html
+		/// </summary>
+		GPSDestLongitude                                   = 22,
+		
+		/// <summary>
+		///     Indicates the reference used for giving the bearing to the destination point. (Hex: 0x0017)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestbearingref.html
+		/// </summary>
+		GPSDestBearingRef                                  = 23,
+		
+		/// <summary>
+		///     Indicates the bearing to the destination point. (Hex: 0x0018)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestbearing.html
+		/// </summary>
+		GPSDestBearing                                     = 24,
+		
+		/// <summary>
+		///     Indicates the unit used to express the distance to the destination point. (Hex: 0x0019)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestdistanceref.html
+		/// </summary>
+		GPSDestDistanceRef                                 = 25,
+		
+		/// <summary>
+		///     Indicates the distance to the destination point. (Hex: 0x001A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdestdistance.html
+		/// </summary>
+		GPSDestDistance                                    = 26,
+		
+		/// <summary>
+		///     A character string recording the name of the method used for location finding. (Hex: 0x001B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsprocessingmethod.html
+		/// </summary>
+		GPSProcessingMethod                                = 27,
+		
+		/// <summary>
+		///     A character string recording the name of the GPS area. (Hex: 0x001C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsareainformation.html
+		/// </summary>
+		GPSAreaInformation                                 = 28,
+		
+		/// <summary>
+		///     A character string recording date and time information relative to UTC (Coordinated Universal Time). (Hex: 0x001D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdatestamp.html
+		/// </summary>
+		GPSDateStamp                                       = 29,
+		
+		/// <summary>
+		///     Indicates whether differential correction is applied to the GPS receiver. (Hex: 0x001E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps/gpsdifferential.html
+		/// </summary>
+		GPSDifferential                                    = 30,
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/IFDEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/IFDEntryTag.cs
new file mode 100644
index 0000000..bbf64a0
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/IFDEntryTag.cs
@@ -0,0 +1,1062 @@
+//
+// IFDEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009-2010 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Entry tags occuring in a Tiff IFD, or IFD0 for Jpegs. They are mostly
+	///    defined by the TIFF specification:
+	///    http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
+	///    The complete overview can be obtained at:
+	///    http://www.awaresystems.be/imaging/tiff.html
+	/// </summary>
+	public enum IFDEntryTag : ushort
+	{
+		
+		/// <summary>
+		///     A general indication of the kind of data contained in this subfile. (Hex: 0x00FE)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/newsubfiletype.html
+		/// </summary>
+		NewSubfileType                                     = 254,
+		
+		/// <summary>
+		///     A general indication of the kind of data contained in this subfile. (Hex: 0x00FF)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/subfiletype.html
+		/// </summary>
+		SubfileType                                        = 255,
+		
+		/// <summary>
+		///     The number of columns in the image, i.e., the number of pixels per row. (Hex: 0x0100)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/imagewidth.html
+		/// </summary>
+		ImageWidth                                         = 256,
+		
+		/// <summary>
+		///     The number of rows of pixels in the image. (Hex: 0x0101)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/imagelength.html
+		/// </summary>
+		ImageLength                                        = 257,
+		
+		/// <summary>
+		///     Number of bits per component. (Hex: 0x0102)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/bitspersample.html
+		/// </summary>
+		BitsPerSample                                      = 258,
+		
+		/// <summary>
+		///     Compression scheme used on the image data. (Hex: 0x0103)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/compression.html
+		/// </summary>
+		Compression                                        = 259,
+		
+		/// <summary>
+		///     The color space of the image data. (Hex: 0x0106)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html
+		/// </summary>
+		PhotometricInterpretation                          = 262,
+		
+		/// <summary>
+		///     For black and white TIFF files that represent shades of gray, the technique used to convert from gray to black and white pixels. (Hex: 0x0107)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/threshholding.html
+		/// </summary>
+		Threshholding                                      = 263,
+		
+		/// <summary>
+		///     The width of the dithering or halftoning matrix used to create a dithered or halftoned bilevel file. (Hex: 0x0108)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/cellwidth.html
+		/// </summary>
+		CellWidth                                          = 264,
+		
+		/// <summary>
+		///     The length of the dithering or halftoning matrix used to create a dithered or halftoned bilevel file. (Hex: 0x0109)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/celllength.html
+		/// </summary>
+		CellLength                                         = 265,
+		
+		/// <summary>
+		///     The logical order of bits within a byte. (Hex: 0x010A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/fillorder.html
+		/// </summary>
+		FillOrder                                          = 266,
+		
+		/// <summary>
+		///     The name of the document from which this image was scanned. (Hex: 0x010D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/documentname.html
+		/// </summary>
+		DocumentName                                       = 269,
+		
+		/// <summary>
+		///     A string that describes the subject of the image. (Hex: 0x010E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/imagedescription.html
+		/// </summary>
+		ImageDescription                                   = 270,
+		
+		/// <summary>
+		///     The scanner manufacturer. (Hex: 0x010F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/make.html
+		/// </summary>
+		Make                                               = 271,
+		
+		/// <summary>
+		///     The scanner model name or number. (Hex: 0x0110)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/model.html
+		/// </summary>
+		Model                                              = 272,
+		
+		/// <summary>
+		///     For each strip, the byte offset of that strip. (Hex: 0x0111)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/stripoffsets.html
+		/// </summary>
+		StripOffsets                                       = 273,
+		
+		/// <summary>
+		///     The orientation of the image with respect to the rows and columns. (Hex: 0x0112)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/orientation.html
+		/// </summary>
+		Orientation                                        = 274,
+		
+		/// <summary>
+		///     The number of components per pixel. (Hex: 0x0115)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/samplesperpixel.html
+		/// </summary>
+		SamplesPerPixel                                    = 277,
+		
+		/// <summary>
+		///     The number of rows per strip. (Hex: 0x0116)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/rowsperstrip.html
+		/// </summary>
+		RowsPerStrip                                       = 278,
+		
+		/// <summary>
+		///     For each strip, the number of bytes in the strip after compression. (Hex: 0x0117)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/stripbytecounts.html
+		/// </summary>
+		StripByteCounts                                    = 279,
+		
+		/// <summary>
+		///     The minimum component value used. (Hex: 0x0118)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/minsamplevalue.html
+		/// </summary>
+		MinSampleValue                                     = 280,
+		
+		/// <summary>
+		///     The maximum component value used. (Hex: 0x0119)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/maxsamplevalue.html
+		/// </summary>
+		MaxSampleValue                                     = 281,
+		
+		/// <summary>
+		///     The number of pixels per ResolutionUnit in the ImageWidth direction. (Hex: 0x011A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/xresolution.html
+		/// </summary>
+		XResolution                                        = 282,
+		
+		/// <summary>
+		///     The number of pixels per ResolutionUnit in the ImageLength direction. (Hex: 0x011B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/yresolution.html
+		/// </summary>
+		YResolution                                        = 283,
+		
+		/// <summary>
+		///     How the components of each pixel are stored. (Hex: 0x011C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/planarconfiguration.html
+		/// </summary>
+		PlanarConfiguration                                = 284,
+		
+		/// <summary>
+		///     The name of the page from which this image was scanned. (Hex: 0x011D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/pagename.html
+		/// </summary>
+		PageName                                           = 285,
+		
+		/// <summary>
+		///     X position of the image. (Hex: 0x011E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/xposition.html
+		/// </summary>
+		XPosition                                          = 286,
+		
+		/// <summary>
+		///     Y position of the image. (Hex: 0x011F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/yposition.html
+		/// </summary>
+		YPosition                                          = 287,
+		
+		/// <summary>
+		///     For each string of contiguous unused bytes in a TIFF file, the byte offset of the string. (Hex: 0x0120)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/freeoffsets.html
+		/// </summary>
+		FreeOffsets                                        = 288,
+		
+		/// <summary>
+		///     For each string of contiguous unused bytes in a TIFF file, the number of bytes in the string. (Hex: 0x0121)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/freebytecounts.html
+		/// </summary>
+		FreeByteCounts                                     = 289,
+		
+		/// <summary>
+		///     The precision of the information contained in the GrayResponseCurve. (Hex: 0x0122)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/grayresponseunit.html
+		/// </summary>
+		GrayResponseUnit                                   = 290,
+		
+		/// <summary>
+		///     For grayscale data, the optical density of each possible pixel value. (Hex: 0x0123)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/grayresponsecurve.html
+		/// </summary>
+		GrayResponseCurve                                  = 291,
+		
+		/// <summary>
+		///     Options for Group 3 Fax compression (Hex: 0x0124)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/t4options.html
+		/// </summary>
+		T4Options                                          = 292,
+		
+		/// <summary>
+		///     Options for Group 4 Fax compression (Hex: 0x0125)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/t6options.html
+		/// </summary>
+		T6Options                                          = 293,
+		
+		/// <summary>
+		///     The unit of measurement for XResolution and YResolution. (Hex: 0x0128)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/resolutionunit.html
+		/// </summary>
+		ResolutionUnit                                     = 296,
+		
+		/// <summary>
+		///     The page number of the page from which this image was scanned. (Hex: 0x0129)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/pagenumber.html
+		/// </summary>
+		PageNumber                                         = 297,
+		
+		/// <summary>
+		///     Describes a transfer function for the image in tabular style. (Hex: 0x012D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/transferfunction.html
+		/// </summary>
+		TransferFunction                                   = 301,
+		
+		/// <summary>
+		///     Name and version number of the software package(s) used to create the image. (Hex: 0x0131)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/software.html
+		/// </summary>
+		Software                                           = 305,
+		
+		/// <summary>
+		///     Date and time of image creation. (Hex: 0x0132)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/datetime.html
+		/// </summary>
+		DateTime                                           = 306,
+		
+		/// <summary>
+		///     Person who created the image. (Hex: 0x013B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/artist.html
+		/// </summary>
+		Artist                                             = 315,
+		
+		/// <summary>
+		///     The computer and/or operating system in use at the time of image creation. (Hex: 0x013C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/hostcomputer.html
+		/// </summary>
+		HostComputer                                       = 316,
+		
+		/// <summary>
+		///     A mathematical operator that is applied to the image data before an encoding scheme is applied. (Hex: 0x013D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html
+		/// </summary>
+		Predictor                                          = 317,
+		
+		/// <summary>
+		///     The chromaticity of the white point of the image. (Hex: 0x013E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/whitepoint.html
+		/// </summary>
+		WhitePoint                                         = 318,
+		
+		/// <summary>
+		///     The chromaticities of the primaries of the image. (Hex: 0x013F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/primarychromaticities.html
+		/// </summary>
+		PrimaryChromaticities                              = 319,
+		
+		/// <summary>
+		///     A color map for palette color images. (Hex: 0x0140)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/colormap.html
+		/// </summary>
+		ColorMap                                           = 320,
+		
+		/// <summary>
+		///     Conveys to the halftone function the range of gray levels within a colorimetrically-specified image that should retain tonal detail. (Hex: 0x0141)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/halftonehints.html
+		/// </summary>
+		HalftoneHints                                      = 321,
+		
+		/// <summary>
+		///     The tile width in pixels. This is the number of columns in each tile. (Hex: 0x0142)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/tilewidth.html
+		/// </summary>
+		TileWidth                                          = 322,
+		
+		/// <summary>
+		///     The tile length (height) in pixels. This is the number of rows in each tile. (Hex: 0x0143)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/tilelength.html
+		/// </summary>
+		TileLength                                         = 323,
+		
+		/// <summary>
+		///     For each tile, the byte offset of that tile, as compressed and stored on disk. (Hex: 0x0144)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/tileoffsets.html
+		/// </summary>
+		TileOffsets                                        = 324,
+		
+		/// <summary>
+		///     For each tile, the number of (compressed) bytes in that tile. (Hex: 0x0145)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/tilebytecounts.html
+		/// </summary>
+		TileByteCounts                                     = 325,
+		
+		/// <summary>
+		///     Used in the TIFF-F standard, denotes the number of 'bad' scan lines encountered by the facsimile device. (Hex: 0x0146)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/badfaxlines.html
+		/// </summary>
+		BadFaxLines                                        = 326,
+		
+		/// <summary>
+		///     Used in the TIFF-F standard, indicates if 'bad' lines encountered during reception are stored in the data, or if 'bad' lines have been replaced by the receiver. (Hex: 0x0147)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/cleanfaxdata.html
+		/// </summary>
+		CleanFaxData                                       = 327,
+		
+		/// <summary>
+		///     Used in the TIFF-F standard, denotes the maximum number of consecutive 'bad' scanlines received. (Hex: 0x0148)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/consecutivebadfaxlines.html
+		/// </summary>
+		ConsecutiveBadFaxLines                             = 328,
+		
+		/// <summary>
+		///     Offset to child IFDs. (Hex: 0x014A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/subifds.html
+		/// </summary>
+		SubIFDs                                            = 330,
+		
+		/// <summary>
+		///     The set of inks used in a separated (PhotometricInterpretation=5) image. (Hex: 0x014C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/inkset.html
+		/// </summary>
+		InkSet                                             = 332,
+		
+		/// <summary>
+		///     The name of each ink used in a separated image. (Hex: 0x014D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/inknames.html
+		/// </summary>
+		InkNames                                           = 333,
+		
+		/// <summary>
+		///     The number of inks. (Hex: 0x014E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/numberofinks.html
+		/// </summary>
+		NumberOfInks                                       = 334,
+		
+		/// <summary>
+		///     The component values that correspond to a 0% dot and 100% dot. (Hex: 0x0150)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/dotrange.html
+		/// </summary>
+		DotRange                                           = 336,
+		
+		/// <summary>
+		///     A description of the printing environment for which this separation is intended. (Hex: 0x0151)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/targetprinter.html
+		/// </summary>
+		TargetPrinter                                      = 337,
+		
+		/// <summary>
+		///     Description of extra components. (Hex: 0x0152)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/extrasamples.html
+		/// </summary>
+		ExtraSamples                                       = 338,
+		
+		/// <summary>
+		///     Specifies how to interpret each data sample in a pixel. (Hex: 0x0153)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/sampleformat.html
+		/// </summary>
+		SampleFormat                                       = 339,
+		
+		/// <summary>
+		///     Specifies the minimum sample value. (Hex: 0x0154)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/sminsamplevalue.html
+		/// </summary>
+		SMinSampleValue                                    = 340,
+		
+		/// <summary>
+		///     Specifies the maximum sample value. (Hex: 0x0155)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/smaxsamplevalue.html
+		/// </summary>
+		SMaxSampleValue                                    = 341,
+		
+		/// <summary>
+		///     Expands the range of the TransferFunction. (Hex: 0x0156)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/transferrange.html
+		/// </summary>
+		TransferRange                                      = 342,
+		
+		/// <summary>
+		///     Mirrors the essentials of PostScript's path creation functionality. (Hex: 0x0157)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/clippath.html
+		/// </summary>
+		ClipPath                                           = 343,
+		
+		/// <summary>
+		///     The number of units that span the width of the image, in terms of integer ClipPath coordinates. (Hex: 0x0158)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/xclippathunits.html
+		/// </summary>
+		XClipPathUnits                                     = 344,
+		
+		/// <summary>
+		///     The number of units that span the height of the image, in terms of integer ClipPath coordinates. (Hex: 0x0159)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/yclippathunits.html
+		/// </summary>
+		YClipPathUnits                                     = 345,
+		
+		/// <summary>
+		///     Aims to broaden the support for indexed images to include support for any color space. (Hex: 0x015A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/indexed.html
+		/// </summary>
+		Indexed                                            = 346,
+		
+		/// <summary>
+		///     JPEG quantization and/or Huffman tables. (Hex: 0x015B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpegtables.html
+		/// </summary>
+		JPEGTables                                         = 347,
+		
+		/// <summary>
+		///     OPI-related. (Hex: 0x015F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/opiproxy.html
+		/// </summary>
+		OPIProxy                                           = 351,
+		
+		/// <summary>
+		///     Used in the TIFF-FX standard to point to an IFD containing tags that are globally applicable to the complete TIFF file. (Hex: 0x0190)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/globalparametersifd.html
+		/// </summary>
+		GlobalParametersIFD                                = 400,
+		
+		/// <summary>
+		///     Used in the TIFF-FX standard, denotes the type of data stored in this file or IFD. (Hex: 0x0191)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/profiletype.html
+		/// </summary>
+		ProfileType                                        = 401,
+		
+		/// <summary>
+		///     Used in the TIFF-FX standard, denotes the 'profile' that applies to this file. (Hex: 0x0192)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/faxprofile.html
+		/// </summary>
+		FaxProfile                                         = 402,
+		
+		/// <summary>
+		///     Used in the TIFF-FX standard, indicates which coding methods are used in the file. (Hex: 0x0193)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/codingmethods.html
+		/// </summary>
+		CodingMethods                                      = 403,
+		
+		/// <summary>
+		///     Used in the TIFF-FX standard, denotes the year of the standard specified by the FaxProfile field. (Hex: 0x0194)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/versionyear.html
+		/// </summary>
+		VersionYear                                        = 404,
+		
+		/// <summary>
+		///     Used in the TIFF-FX standard, denotes the mode of the standard specified by the FaxProfile field. (Hex: 0x0195)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/modenumber.html
+		/// </summary>
+		ModeNumber                                         = 405,
+		
+		/// <summary>
+		///     Used in the TIFF-F and TIFF-FX standards, holds information about the ITULAB (PhotometricInterpretation = 10) encoding. (Hex: 0x01B1)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/decode.html
+		/// </summary>
+		Decode                                             = 433,
+		
+		/// <summary>
+		///     Defined in the Mixed Raster Content part of RFC 2301, is the default color needed in areas where no image is available. (Hex: 0x01B2)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/defaultimagecolor.html
+		/// </summary>
+		DefaultImageColor                                  = 434,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0200)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpegproc.html
+		/// </summary>
+		JPEGProc                                           = 512,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0201)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpeginterchangeformat.html
+		/// </summary>
+		JPEGInterchangeFormat                              = 513,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0202)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpeginterchangeformatlength.html
+		/// </summary>
+		JPEGInterchangeFormatLength                        = 514,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0203)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpegrestartinterval.html
+		/// </summary>
+		JPEGRestartInterval                                = 515,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0205)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpeglosslesspredictors.html
+		/// </summary>
+		JPEGLosslessPredictors                             = 517,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0206)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpegpointtransforms.html
+		/// </summary>
+		JPEGPointTransforms                                = 518,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0207)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpegqtables.html
+		/// </summary>
+		JPEGQTables                                        = 519,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0208)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpegdctables.html
+		/// </summary>
+		JPEGDCTables                                       = 520,
+		
+		/// <summary>
+		///     Old-style JPEG compression field. TechNote2 invalidates this part of the specification. (Hex: 0x0209)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/jpegactables.html
+		/// </summary>
+		JPEGACTables                                       = 521,
+		
+		/// <summary>
+		///     The transformation from RGB to YCbCr image data. (Hex: 0x0211)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/ycbcrcoefficients.html
+		/// </summary>
+		YCbCrCoefficients                                  = 529,
+		
+		/// <summary>
+		///     Specifies the subsampling factors used for the chrominance components of a YCbCr image. (Hex: 0x0212)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/ycbcrsubsampling.html
+		/// </summary>
+		YCbCrSubSampling                                   = 530,
+		
+		/// <summary>
+		///     Specifies the positioning of subsampled chrominance components relative to luminance samples. (Hex: 0x0213)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/ycbcrpositioning.html
+		/// </summary>
+		YCbCrPositioning                                   = 531,
+		
+		/// <summary>
+		///     Specifies a pair of headroom and footroom image data values (codes) for each pixel component. (Hex: 0x0214)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/referenceblackwhite.html
+		/// </summary>
+		ReferenceBlackWhite                                = 532,
+		
+		/// <summary>
+		///     Defined in the Mixed Raster Content part of RFC 2301, used to replace RowsPerStrip for IFDs with variable-sized strips. (Hex: 0x022F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/striprowcounts.html
+		/// </summary>
+		StripRowCounts                                     = 559,
+		
+		/// <summary>
+		///     XML packet containing XMP metadata (Hex: 0x02BC)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/xmp.html
+		/// </summary>
+		XMP                                                = 700,
+		
+		/// <summary>
+		///     Rating tag used by Windows (Hex: 0x4746)
+		/// </summary>
+		Rating                                             = 18246,
+
+		/// <summary>
+		///     Rating tag used by Windows, value in percent (Hex: 0x4749)
+		/// </summary>
+		RatingPercent                                      = 18249,
+
+		/// <summary>
+		///     OPI-related. (Hex: 0x800D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/imageid.html
+		/// </summary>
+		ImageID                                            = 32781,
+		
+		/// <summary>
+		///     Annotation data, as used in 'Imaging for Windows'. (Hex: 0x80A4)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/wangannotation.html
+		/// </summary>
+		WangAnnotation                                     = 32932,
+		
+		/// <summary>
+		///     Copyright notice. (Hex: 0x8298)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/copyright.html
+		/// </summary>
+		Copyright                                          = 33432,
+		
+		/// <summary>
+		///     Specifies the pixel data format encoding in the Molecular Dynamics GEL file format. (Hex: 0x82A5)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdfiletag.html
+		/// </summary>
+		MDFileTag                                          = 33445,
+		
+		/// <summary>
+		///     Specifies a scale factor in the Molecular Dynamics GEL file format. (Hex: 0x82A6)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdscalepixel.html
+		/// </summary>
+		MDScalePixel                                       = 33446,
+		
+		/// <summary>
+		///     Used to specify the conversion from 16bit to 8bit in the Molecular Dynamics GEL file format. (Hex: 0x82A7)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdcolortable.html
+		/// </summary>
+		MDColorTable                                       = 33447,
+		
+		/// <summary>
+		///     Name of the lab that scanned this file, as used in the Molecular Dynamics GEL file format. (Hex: 0x82A8)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdlabname.html
+		/// </summary>
+		MDLabName                                          = 33448,
+		
+		/// <summary>
+		///     Information about the sample, as used in the Molecular Dynamics GEL file format. (Hex: 0x82A9)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdsampleinfo.html
+		/// </summary>
+		MDSampleInfo                                       = 33449,
+		
+		/// <summary>
+		///     Date the sample was prepared, as used in the Molecular Dynamics GEL file format. (Hex: 0x82AA)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdprepdate.html
+		/// </summary>
+		MDPrepDate                                         = 33450,
+		
+		/// <summary>
+		///     Time the sample was prepared, as used in the Molecular Dynamics GEL file format. (Hex: 0x82AB)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdpreptime.html
+		/// </summary>
+		MDPrepTime                                         = 33451,
+		
+		/// <summary>
+		///     Units for data in this file, as used in the Molecular Dynamics GEL file format. (Hex: 0x82AC)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/mdfileunits.html
+		/// </summary>
+		MDFileUnits                                        = 33452,
+		
+		/// <summary>
+		///     Used in interchangeable GeoTIFF files. (Hex: 0x830E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/modelpixelscaletag.html
+		/// </summary>
+		ModelPixelScaleTag                                 = 33550,
+		
+		/// <summary>
+		///     IPTC (International Press Telecommunications Council) metadata. (Hex: 0x83BB)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/iptc.html
+		/// </summary>
+		IPTC                                               = 33723,
+		
+		/// <summary>
+		///     Intergraph Application specific storage. (Hex: 0x847E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/ingrpacketdatatag.html
+		/// </summary>
+		INGRPacketDataTag                                  = 33918,
+		
+		/// <summary>
+		///     Intergraph Application specific flags. (Hex: 0x847F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/ingrflagregisters.html
+		/// </summary>
+		INGRFlagRegisters                                  = 33919,
+		
+		/// <summary>
+		///     Originally part of Intergraph's GeoTIFF tags, but likely understood by IrasB only. (Hex: 0x8480)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/irasbtransformationmatrix.html
+		/// </summary>
+		IrasBTransformationMatrix                          = 33920,
+		
+		/// <summary>
+		///     Originally part of Intergraph's GeoTIFF tags, but now used in interchangeable GeoTIFF files. (Hex: 0x8482)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/modeltiepointtag.html
+		/// </summary>
+		ModelTiepointTag                                   = 33922,
+		
+		/// <summary>
+		///     Used in interchangeable GeoTIFF files. (Hex: 0x85D8)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/modeltransformationtag.html
+		/// </summary>
+		ModelTransformationTag                             = 34264,
+		
+		/// <summary>
+		///     Collection of Photoshop 'Image Resource Blocks'. (Hex: 0x8649)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/photoshop.html
+		/// </summary>
+		Photoshop                                          = 34377,
+		
+		/// <summary>
+		///     A pointer to the Exif IFD. (Hex: 0x8769)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/exififd.html
+		/// </summary>
+		ExifIFD                                            = 34665,
+		
+		/// <summary>
+		///     ICC profile data. (Hex: 0x8773)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/iccprofile.html
+		/// </summary>
+		ICCProfile                                         = 34675,
+		
+		/// <summary>
+		///     Defined in the Mixed Raster Content part of RFC 2301, used to denote the particular function of this Image in the mixed raster scheme. (Hex: 0x87AC)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/imagelayer.html
+		/// </summary>
+		ImageLayer                                         = 34732,
+		
+		/// <summary>
+		///     Used in interchangeable GeoTIFF files. (Hex: 0x87AF)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/geokeydirectorytag.html
+		/// </summary>
+		GeoKeyDirectoryTag                                 = 34735,
+		
+		/// <summary>
+		///     Used in interchangeable GeoTIFF files. (Hex: 0x87B0)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/geodoubleparamstag.html
+		/// </summary>
+		GeoDoubleParamsTag                                 = 34736,
+		
+		/// <summary>
+		///     Used in interchangeable GeoTIFF files. (Hex: 0x87B1)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/geoasciiparamstag.html
+		/// </summary>
+		GeoAsciiParamsTag                                  = 34737,
+		
+		/// <summary>
+		///     A pointer to the Exif-related GPS Info IFD. (Hex: 0x8825)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/gpsifd.html
+		/// </summary>
+		GPSIFD                                             = 34853,
+		
+		/// <summary>
+		///     Used by HylaFAX. (Hex: 0x885C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/hylafaxfaxrecvparams.html
+		/// </summary>
+		HylaFAXFaxRecvParams                               = 34908,
+		
+		/// <summary>
+		///     Used by HylaFAX. (Hex: 0x885D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/hylafaxfaxsubaddress.html
+		/// </summary>
+		HylaFAXFaxSubAddress                               = 34909,
+		
+		/// <summary>
+		///     Used by HylaFAX. (Hex: 0x885E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/hylafaxfaxrecvtime.html
+		/// </summary>
+		HylaFAXFaxRecvTime                                 = 34910,
+		
+		/// <summary>
+		///     Used by Adobe Photoshop. (Hex: 0x935C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/imagesourcedata.html
+		/// </summary>
+		ImageSourceData                                    = 37724,
+		
+		/// <summary>
+		///     A pointer to the Exif-related Interoperability IFD. (Hex: 0xA005)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/interoperabilityifd.html
+		/// </summary>
+		InteroperabilityIFD                                = 40965,
+		
+		/// <summary>
+		///     Used by the GDAL library, holds an XML list of name=value 'metadata' values about the image as a whole, and about specific samples. (Hex: 0xA480)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/gdal_metadata.html
+		/// </summary>
+		GDAL_METADATA                                      = 42112,
+		
+		/// <summary>
+		///     Used by the GDAL library, contains an ASCII encoded nodata or background pixel value. (Hex: 0xA481)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/gdal_nodata.html
+		/// </summary>
+		GDAL_NODATA                                        = 42113,
+		
+		/// <summary>
+		///     Used in the Oce scanning process. (Hex: 0xC427)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/ocescanjobdescription.html
+		/// </summary>
+		OceScanjobDescription                              = 50215,
+		
+		/// <summary>
+		///     Used in the Oce scanning process. (Hex: 0xC428)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/oceapplicationselector.html
+		/// </summary>
+		OceApplicationSelector                             = 50216,
+		
+		/// <summary>
+		///     Used in the Oce scanning process. (Hex: 0xC429)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/oceidentificationnumber.html
+		/// </summary>
+		OceIdentificationNumber                            = 50217,
+		
+		/// <summary>
+		///     Used in the Oce scanning process. (Hex: 0xC42A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/oceimagelogiccharacteristics.html
+		/// </summary>
+		OceImageLogicCharacteristics                       = 50218,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC612)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/dngversion.html
+		/// </summary>
+		DNGVersion                                         = 50706,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC613)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/dngbackwardversion.html
+		/// </summary>
+		DNGBackwardVersion                                 = 50707,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC614)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/uniquecameramodel.html
+		/// </summary>
+		UniqueCameraModel                                  = 50708,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC615)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/localizedcameramodel.html
+		/// </summary>
+		LocalizedCameraModel                               = 50709,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC616)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/cfaplanecolor.html
+		/// </summary>
+		CFAPlaneColor                                      = 50710,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC617)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/cfalayout.html
+		/// </summary>
+		CFALayout                                          = 50711,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC618)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/linearizationtable.html
+		/// </summary>
+		LinearizationTable                                 = 50712,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC619)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/blacklevelrepeatdim.html
+		/// </summary>
+		BlackLevelRepeatDim                                = 50713,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC61A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/blacklevel.html
+		/// </summary>
+		BlackLevel                                         = 50714,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC61B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/blackleveldeltah.html
+		/// </summary>
+		BlackLevelDeltaH                                   = 50715,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC61C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/blackleveldeltav.html
+		/// </summary>
+		BlackLevelDeltaV                                   = 50716,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC61D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/whitelevel.html
+		/// </summary>
+		WhiteLevel                                         = 50717,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC61E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/defaultscale.html
+		/// </summary>
+		DefaultScale                                       = 50718,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC61F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/defaultcroporigin.html
+		/// </summary>
+		DefaultCropOrigin                                  = 50719,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC620)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/defaultcropsize.html
+		/// </summary>
+		DefaultCropSize                                    = 50720,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC621)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/colormatrix1.html
+		/// </summary>
+		ColorMatrix1                                       = 50721,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC622)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/colormatrix2.html
+		/// </summary>
+		ColorMatrix2                                       = 50722,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC623)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/cameracalibration1.html
+		/// </summary>
+		CameraCalibration1                                 = 50723,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC624)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/cameracalibration2.html
+		/// </summary>
+		CameraCalibration2                                 = 50724,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC625)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/reductionmatrix1.html
+		/// </summary>
+		ReductionMatrix1                                   = 50725,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC626)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/reductionmatrix2.html
+		/// </summary>
+		ReductionMatrix2                                   = 50726,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC627)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/analogbalance.html
+		/// </summary>
+		AnalogBalance                                      = 50727,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC628)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/asshotneutral.html
+		/// </summary>
+		AsShotNeutral                                      = 50728,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC629)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/asshotwhitexy.html
+		/// </summary>
+		AsShotWhiteXY                                      = 50729,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC62A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/baselineexposure.html
+		/// </summary>
+		BaselineExposure                                   = 50730,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC62B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/baselinenoise.html
+		/// </summary>
+		BaselineNoise                                      = 50731,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC62C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/baselinesharpness.html
+		/// </summary>
+		BaselineSharpness                                  = 50732,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC62D)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/bayergreensplit.html
+		/// </summary>
+		BayerGreenSplit                                    = 50733,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC62E)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/linearresponselimit.html
+		/// </summary>
+		LinearResponseLimit                                = 50734,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC62F)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/cameraserialnumber.html
+		/// </summary>
+		CameraSerialNumber                                 = 50735,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC630)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/lensinfo.html
+		/// </summary>
+		LensInfo                                           = 50736,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC631)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/chromablurradius.html
+		/// </summary>
+		ChromaBlurRadius                                   = 50737,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC632)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/antialiasstrength.html
+		/// </summary>
+		AntiAliasStrength                                  = 50738,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC634)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/dngprivatedata.html
+		/// </summary>
+		DNGPrivateData                                     = 50740,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC635)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/makernotesafety.html
+		/// </summary>
+		MakerNoteSafety                                    = 50741,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC65A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/calibrationilluminant1.html
+		/// </summary>
+		CalibrationIlluminant1                             = 50778,
+		
+		/// <summary>
+		///     Used in IFD 0 of DNG files. (Hex: 0xC65B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/calibrationilluminant2.html
+		/// </summary>
+		CalibrationIlluminant2                             = 50779,
+		
+		/// <summary>
+		///     Used in Raw IFD of DNG files. (Hex: 0xC65C)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/bestqualityscale.html
+		/// </summary>
+		BestQualityScale                                   = 50780,
+		
+		/// <summary>
+		///     Alias Sketchbook Pro layer usage description. (Hex: 0xC660)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/aliaslayermetadata.html
+		/// </summary>
+		AliasLayerMetadata                                 = 50784,
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/IOPEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/IOPEntryTag.cs
new file mode 100644
index 0000000..f2c0278
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/IOPEntryTag.cs
@@ -0,0 +1,61 @@
+//
+// IOPEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009-2010 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Entry tags occuring in the Interoperability IFD
+	///    The complete overview can be obtained at:
+	///    http://www.awaresystems.be/imaging/tiff.html
+	/// </summary>
+	public enum IOPEntryTag : ushort
+	{
+		/// <summary>
+		///     Indicates the identification of the Interoperability rule. (Hex: 0x0001)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/interoperability/interoperabilityindex.html
+		/// </summary>
+		InteroperabilityIndex                              = 1,
+
+		/// <summary>
+		///     Interoperability version. (Hex: 0x0002)
+		/// </summary>
+		InteroperabilityVersion                            = 2,
+
+		/// <summary>
+		///     File format of image file. (Hex: 0x1000)
+		/// </summary>
+		RelatedImageFileFormat                             = 4096,
+
+		/// <summary>
+		///     Image Width. (Hex: 0x1001)
+		/// </summary>
+		RelatedImageWidth                                  = 4097,
+
+		/// <summary>
+		///     Image Height. (Hex: 0x1002)
+		/// </summary>
+		RelatedImageLength                                 = 4098,
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/Nikon3MakerNoteEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/Nikon3MakerNoteEntryTag.cs
new file mode 100644
index 0000000..4a28bc2
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/Nikon3MakerNoteEntryTag.cs
@@ -0,0 +1,473 @@
+//
+// Nikon3MakerNoteEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2010 Ruben Vermeersch
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Nikon format 3 makernote tags.
+	///    Based on http://www.exiv2.org/tags-nikon.html and
+	///    http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+	/// </summary>
+	public enum Nikon3MakerNoteEntryTag : ushort
+	{
+		/// <summary>
+		///    Makernote version. (Hex: 0x0001)
+		/// </summary>
+		Version                                             = 1,
+
+		/// <summary>
+		///    ISO speed setting. (Hex: 0X0002)
+		/// </summary>
+		ISOSpeed                                            = 2,
+
+		/// <summary>
+		///    Color mode. (Hex: 0X0003)
+		/// </summary>
+		ColorMode                                           = 3,
+
+		/// <summary>
+		///    Image quality setting. (Hex: 0X0004)
+		/// </summary>
+		Quality                                             = 4,
+
+		/// <summary>
+		///    White balance. (Hex: 0X0005)
+		/// </summary>
+		WhiteBalance                                        = 5,
+
+		/// <summary>
+		///    Image sharpening setting. (Hex: 0X0006)
+		/// </summary>
+		Sharpening                                          = 6,
+
+		/// <summary>
+		///    Focus mode. (Hex: 0X0007)
+		/// </summary>
+		Focus                                               = 7,
+
+		/// <summary>
+		///    Flash setting. (Hex: 0X0008)
+		/// </summary>
+		FlashSetting                                        = 8,
+
+		/// <summary>
+		///    Flash device. (Hex: 0X0009)
+		/// </summary>
+		FlashDevice                                         = 9,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X000A)
+		/// </summary>
+		Unknown10                                           = 10,
+
+		/// <summary>
+		///    White balance bias. (Hex: 0X000B)
+		/// </summary>
+		WhiteBalanceBias                                    = 11,
+
+		/// <summary>
+		///    WB RB levels. (Hex: 0X000C)
+		/// </summary>
+		WB_RBLevels                                         = 12,
+
+		/// <summary>
+		///    Program shift. (Hex: 0X000D)
+		/// </summary>
+		ProgramShift                                        = 13,
+
+		/// <summary>
+		///    Exposure difference. (Hex: 0X000E)
+		/// </summary>
+		ExposureDiff                                        = 14,
+
+		/// <summary>
+		///    ISO selection. (Hex: 0X000F)
+		/// </summary>
+		ISOSelection                                        = 15,
+
+		/// <summary>
+		///    Data dump. (Hex: 0X0010)
+		/// </summary>
+		DataDump                                            = 16,
+
+		/// <summary>
+		///    Offset to an IFD containing a preview image. (Hex: 0x0011)
+		/// </summary>
+		Preview                                             = 17,
+
+		/// <summary>
+		///    Flash compensation setting. (Hex: 0X0012)
+		/// </summary>
+		FlashComp                                           = 18,
+
+		/// <summary>
+		///    ISO setting. (Hex: 0X0013)
+		/// </summary>
+		ISOSettings                                         = 19,
+
+		/// <summary>
+		///    Image boundary. (Hex: 0X0016)
+		/// </summary>
+		ImageBoundary                                       = 22,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X0017)
+		/// </summary>
+		Unknown23                                           = 23,
+
+		/// <summary>
+		///    Flash bracket compensation applied. (Hex: 0X0018)
+		/// </summary>
+		FlashBracketComp                                    = 24,
+
+		/// <summary>
+		///    AE bracket compensation applied. (Hex: 0X0019)
+		/// </summary>
+		ExposureBracketComp                                 = 25,
+
+		/// <summary>
+		///    Image processing. (Hex: 0X001A)
+		/// </summary>
+		ImageProcessing                                     = 26,
+
+		/// <summary>
+		///    Crop high speed. (Hex: 0X001B)
+		/// </summary>
+		CropHiSpeed                                         = 27,
+
+		/// <summary>
+		///    Serial Number. (Hex: 0X001D)
+		/// </summary>
+		SerialNumber                                        = 29,
+
+		/// <summary>
+		///    Color space. (Hex: 0X001E)
+		/// </summary>
+		ColorSpace                                          = 30,
+
+		/// <summary>
+		///    VR info. (Hex: 0X001F)
+		/// </summary>
+		VRInfo                                              = 31,
+
+		/// <summary>
+		///    Image authentication. (Hex: 0X0020)
+		/// </summary>
+		ImageAuthentication                                 = 32,
+
+		/// <summary>
+		///    ActiveD-lighting. (Hex: 0X0022)
+		/// </summary>
+		ActiveDLighting                                     = 34,
+
+		/// <summary>
+		///    Picture control. (Hex: 0X0023)
+		/// </summary>
+		PictureControl                                      = 35,
+
+		/// <summary>
+		///    World time. (Hex: 0X0024)
+		/// </summary>
+		WorldTime                                           = 36,
+
+		/// <summary>
+		///    ISO info. (Hex: 0X0025)
+		/// </summary>
+		ISOInfo                                             = 37,
+
+		/// <summary>
+		///    Vignette control. (Hex: 0X002A)
+		/// </summary>
+		VignetteControl                                     = 42,
+
+		/// <summary>
+		///    Image adjustment setting. (Hex: 0X0080)
+		/// </summary>
+		ImageAdjustment                                     = 128,
+
+		/// <summary>
+		///    Tone compensation. (Hex: 0X0081)
+		/// </summary>
+		ToneComp                                            = 129,
+
+		/// <summary>
+		///    Auxiliary lens (adapter). (Hex: 0X0082)
+		/// </summary>
+		AuxiliaryLens                                       = 130,
+
+		/// <summary>
+		///    Lens type. (Hex: 0X0083)
+		/// </summary>
+		LensType                                            = 131,
+
+		/// <summary>
+		///    Lens. (Hex: 0X0084)
+		/// </summary>
+		Lens                                                = 132,
+
+		/// <summary>
+		///    Manual focus distance. (Hex: 0X0085)
+		/// </summary>
+		FocusDistance                                       = 133,
+
+		/// <summary>
+		///    Digital zoom setting. (Hex: 0X0086)
+		/// </summary>
+		DigitalZoom                                         = 134,
+
+		/// <summary>
+		///    Mode of flash used. (Hex: 0X0087)
+		/// </summary>
+		FlashMode                                           = 135,
+
+		/// <summary>
+		///    AF info. (Hex: 0X0088)
+		/// </summary>
+		AFInfo                                              = 136,
+
+		/// <summary>
+		///    Shooting mode. (Hex: 0X0089)
+		/// </summary>
+		ShootingMode                                        = 137,
+
+		/// <summary>
+		///    Auto bracket release. (Hex: 0X008A)
+		/// </summary>
+		AutoBracketRelease                                  = 138,
+
+		/// <summary>
+		///    Lens FStops. (Hex: 0X008B)
+		/// </summary>
+		LensFStops                                          = 139,
+
+		/// <summary>
+		///    Contrast curve. (Hex: 0X008C)
+		/// </summary>
+		ContrastCurve                                       = 140,
+
+		/// <summary>
+		///    Color hue. (Hex: 0X008D)
+		/// </summary>
+		ColorHue                                            = 141,
+
+		/// <summary>
+		///    Scene mode. (Hex: 0X008F)
+		/// </summary>
+		SceneMode                                           = 143,
+
+		/// <summary>
+		///    Light source. (Hex: 0X0090)
+		/// </summary>
+		LightSource                                         = 144,
+
+		/// <summary>
+		///    Shot info. (Hex: 0X0091)
+		/// </summary>
+		ShotInfo                                            = 145,
+
+		/// <summary>
+		///    Hue adjustment. (Hex: 0X0092)
+		/// </summary>
+		HueAdjustment                                       = 146,
+
+		/// <summary>
+		///    NEF compression. (Hex: 0X0093)
+		/// </summary>
+		NEFCompression                                      = 147,
+
+		/// <summary>
+		///    Saturation. (Hex: 0X0094)
+		/// </summary>
+		Saturation                                          = 148,
+
+		/// <summary>
+		///    Noise reduction. (Hex: 0X0095)
+		/// </summary>
+		NoiseReduction                                      = 149,
+
+		/// <summary>
+		///    Linearization table. (Hex: 0X0096)
+		/// </summary>
+		LinearizationTable                                  = 150,
+
+		/// <summary>
+		///    Color balance. (Hex: 0X0097)
+		/// </summary>
+		ColorBalance                                        = 151,
+
+		/// <summary>
+		///    Lens data settings. (Hex: 0X0098)
+		/// </summary>
+		LensData                                            = 152,
+
+		/// <summary>
+		///    Raw image center. (Hex: 0X0099)
+		/// </summary>
+		RawImageCenter                                      = 153,
+
+		/// <summary>
+		///    Sensor pixel size. (Hex: 0X009A)
+		/// </summary>
+		SensorPixelSize                                     = 154,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X009B)
+		/// </summary>
+		Unknown155                                          = 155,
+
+		/// <summary>
+		///    Scene assist. (Hex: 0X009C)
+		/// </summary>
+		SceneAssist                                         = 156,
+
+		/// <summary>
+		///    Retouch history. (Hex: 0X009E)
+		/// </summary>
+		RetouchHistory                                      = 158,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X009F)
+		/// </summary>
+		Unknown159                                          = 159,
+
+		/// <summary>
+		///    Camera serial number, usually starts with "NO= ". (Hex: 0X00A0)
+		/// </summary>
+		SerialNO                                            = 160,
+
+		/// <summary>
+		///    Image data size. (Hex: 0X00A2)
+		/// </summary>
+		ImageDataSize                                       = 162,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X00A3)
+		/// </summary>
+		Unknown163                                          = 163,
+
+		/// <summary>
+		///    Image count. (Hex: 0X00A5)
+		/// </summary>
+		ImageCount                                          = 165,
+
+		/// <summary>
+		///    Deleted image count. (Hex: 0X00A6)
+		/// </summary>
+		DeletedImageCount                                   = 166,
+
+		/// <summary>
+		///    Number of shots taken by camera. (Hex: 0X00A7)
+		/// </summary>
+		ShutterCount                                        = 167,
+
+		/// <summary>
+		///    Flash info. (Hex: 0X00A8)
+		/// </summary>
+		FlashInfo                                           = 168,
+
+		/// <summary>
+		///    Image optimization. (Hex: 0X00A9)
+		/// </summary>
+		ImageOptimization                                   = 169,
+
+		/// <summary>
+		///    Saturation. (Hex: 0X00AA)
+		/// </summary>
+		Saturation2                                         = 170,
+
+		/// <summary>
+		///    Program variation. (Hex: 0X00AB)
+		/// </summary>
+		VariProgram                                         = 171,
+
+		/// <summary>
+		///    Image stabilization. (Hex: 0X00AC)
+		/// </summary>
+		ImageStabilization                                  = 172,
+
+		/// <summary>
+		///    AF response. (Hex: 0X00AD)
+		/// </summary>
+		AFResponse                                          = 173,
+
+		/// <summary>
+		///    Multi exposure. (Hex: 0X00B0)
+		/// </summary>
+		MultiExposure                                       = 176,
+
+		/// <summary>
+		///    High ISO Noise Reduction. (Hex: 0X00B1)
+		/// </summary>
+		HighISONoiseReduction                               = 177,
+
+		/// <summary>
+		///    Toning effect. (Hex: 0X00B3)
+		/// </summary>
+		ToningEffect                                        = 179,
+
+		/// <summary>
+		///    AF info 2. (Hex: 0X00B7)
+		/// </summary>
+		AFInfo2                                             = 183,
+
+		/// <summary>
+		///    File info. (Hex: 0X00B8)
+		/// </summary>
+		FileInfo                                            = 184,
+
+		/// <summary>
+		///    PrintIM information. (Hex: 0X0E00)
+		/// </summary>
+		PrintIM                                             = 3584,
+
+		/// <summary>
+		///    Capture data. (Hex: 0X0E01)
+		/// </summary>
+		CaptureData                                         = 3585,
+
+		/// <summary>
+		///    Capture version. (Hex: 0X0E09)
+		/// </summary>
+		CaptureVersion                                      = 3593,
+
+		/// <summary>
+		///    Capture offsets. (Hex: 0X0E0E)
+		/// </summary>
+		CaptureOffsets                                      = 3598,
+
+		/// <summary>
+		///    Scan IFD. (Hex: 0X0E10)
+		/// </summary>
+		ScanIFD                                             = 3600,
+
+		/// <summary>
+		///    ICC profile. (Hex: 0X0E1D)
+		/// </summary>
+		ICCProfile                                          = 3613,
+
+		/// <summary>
+		///    Capture output. (Hex: 0X0E1E)
+		/// </summary>
+		CaptureOutput                                       = 3614,
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/NikonPreviewMakerNoteEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/NikonPreviewMakerNoteEntryTag.cs
new file mode 100644
index 0000000..91b828c
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/NikonPreviewMakerNoteEntryTag.cs
@@ -0,0 +1,80 @@
+//
+// NikonPreviewMakerNoteEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009-2010 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Nikon makernote preview image tags
+	///    The preview image is contained in a sub-IFD stored by the tag
+	///    Nikon3MakerNoteEntryTag.Preview.
+	///    Based on:
+	///    http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#PreviewImage
+	/// </summary>
+	public enum NikonPreviewMakerNoteEntryTag : ushort
+	{
+
+		/// <summary>
+		///     Compression scheme used on the image data. (Hex: 0x0103)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/compression.html
+		/// </summary>
+		Compression                                        = 259,
+
+		/// <summary>
+		///     The number of pixels per ResolutionUnit in the ImageWidth direction. (Hex: 0x011A)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/xresolution.html
+		/// </summary>
+		XResolution                                        = 282,
+		
+		/// <summary>
+		///     The number of pixels per ResolutionUnit in the ImageLength direction. (Hex: 0x011B)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/yresolution.html
+		/// </summary>
+		YResolution                                        = 283,
+
+		/// <summary>
+		///     The unit of measurement for XResolution and YResolution. (Hex: 0x0128)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/resolutionunit.html
+		/// </summary>
+		ResolutionUnit                                     = 296,
+		
+		/// <summary>
+		///     Start of the preview image data. (Hex: 0x0201)
+		///     http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#PreviewImage
+		/// </summary>
+		PreviewImageStart                                  = 513,
+		
+		/// <summary>
+		///     Length of the preview image data. (Hex: 0x0202)
+		///     http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#PreviewImage
+		/// </summary>
+		PreviewImageLength                                 = 514,
+		
+		/// <summary>
+		///     Specifies the positioning of subsampled chrominance components relative to luminance samples. (Hex: 0x0213)
+		///     http://www.awaresystems.be/imaging/tiff/tifftags/ycbcrpositioning.html
+		/// </summary>
+		YCbCrPositioning                                   = 531
+	}
+}
diff --git a/lib/TagLib/TagLib/IFD/Tags/PanasonicMakerNoteEntryTag.cs b/lib/TagLib/TagLib/IFD/Tags/PanasonicMakerNoteEntryTag.cs
new file mode 100644
index 0000000..b460870
--- /dev/null
+++ b/lib/TagLib/TagLib/IFD/Tags/PanasonicMakerNoteEntryTag.cs
@@ -0,0 +1,290 @@
+//
+// PanasonicMakerNoteEntryTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2010 Ruben Vermeersch
+//
+// 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
+//
+
+namespace TagLib.IFD.Tags
+{
+	/// <summary>
+	///    Panasonic makernote tags.
+	///    Based on http://www.exiv2.org/tags-panasonic.html
+	/// </summary>
+	public enum PanasonicMakerNoteEntryTag : ushort
+	{
+		/// <summary>
+		///    Image Quality. (Hex: 0x0001)
+		/// </summary>
+		Quality                                             = 1,
+
+		/// <summary>
+		///    Firmware version. (Hex: 0X0002)
+		/// </summary>
+		FirmwareVersion                                     = 2,
+
+		/// <summary>
+		///    White balance setting. (Hex: 0X0003)
+		/// </summary>
+		WhiteBalance                                        = 3,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X0004)
+		/// </summary>
+		Unknown4                                            = 4,
+
+		/// <summary>
+		///    Focus mode. (Hex: 0X0007)
+		/// </summary>
+		FocusMode                                           = 7,
+
+		/// <summary>
+		///    AF mode. (Hex: 0X000F)
+		/// </summary>
+		AFMode                                              = 15,
+
+		/// <summary>
+		///    Image stabilization. (Hex: 0X001A)
+		/// </summary>
+		ImageStabilization                                  = 26,
+
+		/// <summary>
+		///    Macro mode. (Hex: 0X001C)
+		/// </summary>
+		Macro                                               = 28,
+
+		/// <summary>
+		///    Shooting mode. (Hex: 0X001F)
+		/// </summary>
+		ShootingMode                                        = 31,
+
+		/// <summary>
+		///    Audio. (Hex: 0X0020)
+		/// </summary>
+		Audio                                               = 32,
+
+		/// <summary>
+		///    Data dump. (Hex: 0X0021)
+		/// </summary>
+		DataDump                                            = 33,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X0022)
+		/// </summary>
+		Unknown34                                           = 34,
+
+		/// <summary>
+		///    White balance adjustment. (Hex: 0X0023)
+		/// </summary>
+		WhiteBalanceBias                                    = 35,
+
+		/// <summary>
+		///    Flash bias. (Hex: 0X0024)
+		/// </summary>
+		FlashBias                                           = 36,
+
+		/// <summary>
+		///    This number is unique, and contains the date of manufacture, but
+		///    is not the same as the number printed on the camera body. 
+		///    (Hex: 0X0025)
+		/// </summary>
+		InternalSerialNumber                                = 37,
+
+		/// <summary>
+		///    Exif version. (Hex: 0X0026)
+		/// </summary>
+		ExifVersion                                         = 38,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X0027)
+		/// </summary>
+		Unknown39                                           = 39,
+
+		/// <summary>
+		///    Color effect. (Hex: 0X0028)
+		/// </summary>
+		ColorEffect                                         = 40,
+
+		/// <summary>
+		///    Time in 1/100s from when the camera was powered on to when the
+		///    image is written to memory card. (Hex: 0X0029)
+		/// </summary>
+		TimeSincePowerOn                                    = 41,
+
+		/// <summary>
+		///    Burst mode. (Hex: 0X002A)
+		/// </summary>
+		BurstMode                                           = 42,
+
+		/// <summary>
+		///    Sequence number. (Hex: 0X002B)
+		/// </summary>
+		SequenceNumber                                      = 43,
+
+		/// <summary>
+		///    Contrast setting. (Hex: 0X002C)
+		/// </summary>
+		Contrast                                            = 44,
+
+		/// <summary>
+		///    Noise reduction. (Hex: 0X002D)
+		/// </summary>
+		NoiseReduction                                      = 45,
+
+		/// <summary>
+		///    Self timer. (Hex: 0X002E)
+		/// </summary>
+		SelfTimer                                           = 46,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X002F)
+		/// </summary>
+		Unknown47                                           = 47,
+
+		/// <summary>
+		///    Rotation. (Hex: 0X0030)
+		/// </summary>
+		Rotation                                            = 48,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X0031)
+		/// </summary>
+		Unknown49                                           = 49,
+
+		/// <summary>
+		///    Color mode. (Hex: 0X0032)
+		/// </summary>
+		ColorMode                                           = 50,
+
+		/// <summary>
+		///    Baby (or pet) age. (Hex: 0X0033)
+		/// </summary>
+		BabyAge                                             = 51,
+
+		/// <summary>
+		///    Optical zoom mode. (Hex: 0X0034)
+		/// </summary>
+		OpticalZoomMode                                     = 52,
+
+		/// <summary>
+		///    Conversion lens. (Hex: 0X0035)
+		/// </summary>
+		ConversionLens                                      = 53,
+
+		/// <summary>
+		///    Travel day. (Hex: 0X0036)
+		/// </summary>
+		TravelDay                                           = 54,
+
+		/// <summary>
+		///    Contrast. (Hex: 0X0039)
+		/// </summary>
+		Contrast2                                           = 57,
+
+		/// <summary>
+		///    World time location. (Hex: 0X003A)
+		/// </summary>
+		WorldTimeLocation                                   = 58,
+
+		/// <summary>
+		///    Program ISO. (Hex: 0X003C)
+		/// </summary>
+		ProgramISO                                          = 60,
+
+		/// <summary>
+		///    Saturation. (Hex: 0X0040)
+		/// </summary>
+		Saturation                                          = 64,
+
+		/// <summary>
+		///    Sharpness. (Hex: 0X0041)
+		/// </summary>
+		Sharpness                                           = 65,
+
+		/// <summary>
+		///    Film mode. (Hex: 0X0042)
+		/// </summary>
+		FilmMode                                            = 66,
+
+		/// <summary>
+		///    WB adjust AB. Positive is a shift toward blue. (Hex: 0X0046)
+		/// </summary>
+		WBAdjustAB                                          = 70,
+
+		/// <summary>
+		///    WBAdjustGM. Positive is a shift toward green. (Hex: 0X0047)
+		/// </summary>
+		WBAdjustGM                                          = 71,
+
+		/// <summary>
+		///    Lens type. (Hex: 0X0051)
+		/// </summary>
+		LensType                                            = 81,
+
+		/// <summary>
+		///    Lens serial number. (Hex: 0X0052)
+		/// </summary>
+		LensSerialNumber                                    = 82,
+
+		/// <summary>
+		///    Accessory type. (Hex: 0X0053)
+		/// </summary>
+		AccessoryType                                       = 83,
+
+		/// <summary>
+		///    PrintIM information. (Hex: 0X0E00)
+		/// </summary>
+		PrintIM                                             = 3584,
+
+		/// <summary>
+		///    Unknown. (Hex: 0X4449)
+		/// </summary>
+		Unknown17481                                        = 17481,
+
+		/// <summary>
+		///    MakerNote version. (Hex: 0X8000)
+		/// </summary>
+		MakerNoteVersion                                    = 32768,
+
+		/// <summary>
+		///    Scene mode. (Hex: 0X8001)
+		/// </summary>
+		SceneMode                                           = 32769,
+
+		/// <summary>
+		///    WB red level. (Hex: 0X8004)
+		/// </summary>
+		WBRedLevel                                          = 32772,
+
+		/// <summary>
+		///    WB green level. (Hex: 0X8005)
+		/// </summary>
+		WBGreenLevel                                        = 32773,
+
+		/// <summary>
+		///    WB blue level. (Hex: 0X8006)
+		/// </summary>
+		WBBlueLevel                                         = 32774,
+
+		/// <summary>
+		///    Baby (or pet) age. (Hex: 0X8010)
+		/// </summary>
+		BabyAge2                                            = 32784,
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v1/StringHandler.cs b/lib/TagLib/TagLib/Id3v1/StringHandler.cs
new file mode 100644
index 0000000..f66159c
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v1/StringHandler.cs
@@ -0,0 +1,77 @@
+//
+// StringHandler.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v1tag.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v1
+{
+	/// <summary>
+	///    This class provides a mechanism for customizing how Id3v1 text
+	///    is read and written.
+	/// </summary>
+	public class StringHandler
+	{
+		/// <summary>
+		///    Converts raw ID3v1 text data to a <see cref="string" />
+		///    object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing raw Id3v1
+		///    text data.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string"/> object containing the converted
+		///    text.
+		/// </returns>
+		public virtual string Parse (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			string output = data.ToString (StringType.Latin1).Trim ();
+			int i = output.IndexOf ('\0');
+			return (i >= 0) ? output.Substring (0, i) : output;
+		}
+		
+		/// <summary>
+		///    Converts a <see cref="string" /> object to raw ID3v1 text
+		///    data.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string" /> object to convert.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> containing the raw ID3v1 text
+		///    data.
+		/// </returns>
+		public virtual ByteVector Render (string text)
+		{
+			return ByteVector.FromString (text, StringType.Latin1);
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v1/Tag.cs b/lib/TagLib/TagLib/Id3v1/Tag.cs
new file mode 100644
index 0000000..3283607
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v1/Tag.cs
@@ -0,0 +1,500 @@
+//
+// Tag.cs: Provide support for reading and writing ID3v1 tags.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v1tag.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+using System.Globalization;
+
+namespace TagLib.Id3v1
+{
+	/// <summary>
+	///    This class extends <see cref="Tag" /> to provide support for
+	///    reading and writing tags stored in the ID3v1.1 format.
+	/// </summary>
+	public class Tag : TagLib.Tag
+	{
+#region Private Static Fields
+		
+		private static StringHandler string_handler = new StringHandler ();
+		
+#endregion
+
+
+
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the title.
+		/// </summary>
+		private string title;
+		
+		/// <summary>
+		///    Contains the semicolon separated performers.
+		/// </summary>
+		private string artist;
+		
+		/// <summary>
+		///    Contains the album name.
+		/// </summary>
+		private string album;
+		
+		/// <summary>
+		///    Contains the 4 digit year.
+		/// </summary>
+		private string year;
+		
+		/// <summary>
+		///    Contains a comment on track.
+		/// </summary>
+		private string comment;
+		
+		/// <summary>
+		///    Contains the track number in the album.
+		/// </summary>
+		private byte track;
+		
+		/// <summary>
+		///    Contains the genre index.
+		/// </summary>
+		private byte genre;
+		
+#endregion
+
+
+
+
+#region Public Static Fields
+		
+		/// <summary>
+		///    The size of a ID3v1 tag.
+		/// </summary>
+		public const uint Size = 128;
+		
+		/// <summary>
+		///    The identifier used to recognize a ID3v1 tags.
+		/// </summary>
+		/// <value>
+		///    "TAG"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "TAG";
+
+#endregion
+
+
+
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> with no contents.
+		/// </summary>
+		public Tag ()
+		{
+			Clear ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> by reading the contents from a specified
+		///    position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file from
+		///    which the contents of the new instance is to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the tag.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The file does not contain <see cref="FileIdentifier" />
+		///    at the given position.
+		/// </exception>
+		public Tag (File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Mode = TagLib.File.AccessMode.Read;
+			
+			if (position < 0 ||
+				position > file.Length - Size)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			file.Seek (position);
+			
+			// read the tag -- always 128 bytes
+			
+			ByteVector data = file.ReadBlock ((int) Size);
+			
+			// some initial sanity checking
+			
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"ID3v1 data does not start with identifier.");
+			
+			Parse (data);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> by reading the contents from a specified
+		///    <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object to read the tag from.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> is less than 128 bytes or does
+		///    not start with <see cref="FileIdentifier" />.
+		/// </exception>
+		public Tag (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"ID3v1 data is less than 128 bytes long.");
+			
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"ID3v1 data does not start with identifier.");
+			
+			Parse (data);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ID3v1 tag.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered tag.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			
+			data.Add (FileIdentifier);
+			data.Add (string_handler.Render (title  ).Resize (30));
+			data.Add (string_handler.Render (artist ).Resize (30));
+			data.Add (string_handler.Render (album  ).Resize (30));
+			data.Add (string_handler.Render (year   ).Resize ( 4));
+			data.Add (string_handler.Render (comment).Resize (28));
+			data.Add ((byte) 0);
+			data.Add (track);
+			data.Add (genre);
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Properties
+		
+		/// <summary>
+		///    Gets and sets the <see cref="StringHandler" /> object
+		///    to use when reading and writing ID3v1 fields.
+		/// </summary>
+		/// <value>
+		///    A <see cref="StringHandler" /> object to use when
+		///    processing fields.
+		/// </value>
+		public static StringHandler DefaultStringHandler {
+			get {return string_handler;}
+			set {string_handler = value;}
+		}
+		
+#endregion
+		
+		
+#region Private Methods
+		
+		/// <summary>
+		///    Populates the current instance by parsing the contents of
+		///    a raw ID3v1 tag.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    starting with an ID3v1 tag.
+		/// </param>
+		private void Parse (ByteVector data)
+		{
+			title  = string_handler.Parse (data.Mid ( 3, 30));
+			artist = string_handler.Parse (data.Mid (33, 30));
+			album  = string_handler.Parse (data.Mid (63, 30));
+			year   = string_handler.Parse (data.Mid (93,  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 [125] == 0 && data [126] != 0) {
+				// ID3v1.1 detected
+				comment = string_handler.Parse (data.Mid (97, 28));
+				track = data [126];
+			} else {
+				comment = string_handler.Parse (data.Mid (97, 30));
+			}
+			
+			genre = data [127];
+		}
+		
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.Id3v1" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.Id3v1;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    When stored on disk, only the first 30 bytes of the
+		///    Latin-1 encoded value will be stored. This may result in
+		///    lost data.
+		/// </remarks>
+		public override string Title {
+			get {
+				return string.IsNullOrEmpty (title) ?
+					null : title;
+			}
+			set {
+				title = value != null ?
+					value.Trim () : String.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    When stored on disk, only the first 30 bytes of the
+		///    Latin-1 encoded value will be stored, minus a byte for
+		///    each additionial performer (i.e. two performers will only
+		///    have 29 bytes and three performers will only have 28
+		///    bytes).This may result in lost data.
+		/// </remarks>
+		public override string [] Performers {
+			get {
+				return string.IsNullOrEmpty (artist) ?
+					new string [0] : artist.Split (';');
+			}
+			set {
+				artist = value != null ?
+					string.Join (";", value) : string.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    When stored on disk, only the first 30 bytes of the
+		///    Latin-1 encoded value will be stored. This may result in
+		///    lost data.
+		/// </remarks>
+		public override string Album {
+			get {
+				return string.IsNullOrEmpty (album) ?
+					null : album;
+			}
+			set {
+				album = value != null ?
+					value.Trim () : String.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    When stored on disk, only the first 28 bytes of the
+		///    Latin-1 encoded value will be stored. This may result in
+		///    lost data.
+		/// </remarks>
+		public override string Comment {
+			get {
+				return string.IsNullOrEmpty (comment) ?
+					null : comment;
+			}
+			set {
+				comment = value != null ?
+					value.Trim () : String.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    Only first genre will be stored and only if it is an
+		///    exact match for a value appearing in <see
+		///    cref="TagLib.Genres.Audio" />. All other values will
+		///    result in the property being cleared.
+		/// </remarks>
+		public override string [] Genres {
+			get {
+				string genre_name =
+					TagLib.Genres.IndexToAudio (genre);
+				
+				return (genre_name != null) ?
+					new string [] {genre_name} :
+					new string [0];
+			}
+			set {
+				genre = (value == null || value.Length == 0) ?
+					(byte) 255 :
+					TagLib.Genres.AudioToIndex (
+						value [0].Trim ());
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    Only values between 1 and 9999 will be stored, all other
+		///    values will result in the property being zeroed.
+		/// </remarks>
+		public override uint Year {
+			get {
+				uint value;
+				return uint.TryParse (year,
+					NumberStyles.Integer,
+					CultureInfo.InvariantCulture,
+					out value) ? value : 0;
+			}
+			
+			set {
+				year = (value > 0 && value < 10000) ?
+					value.ToString (
+						CultureInfo.InvariantCulture) :
+					String.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    Only values between 1 and 255 will be stored, all other
+		///    values will result in the property being zeroed.
+		/// </remarks>
+		public override uint Track {
+			get {return track;}
+			set {track = (byte) (value < 256 ? value : 0);}
+		}
+
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			title = artist = album = year = comment = null;
+			track = 0;
+			genre = 255;
+		}
+
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/ExtendedHeader.cs b/lib/TagLib/TagLib/Id3v2/ExtendedHeader.cs
new file mode 100644
index 0000000..f39d733
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/ExtendedHeader.cs
@@ -0,0 +1,122 @@
+//
+// ExtendedHeader.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v2extendedheader.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    This class is a filler until support for reading and writing the
+	///    ID3v2 extended header is implemented.
+	/// </summary>
+	public class ExtendedHeader : ICloneable
+	{
+		/// <summary>
+		///    Contains the size of the read header.
+		/// </summary>
+		private uint size;
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ExtendedHeader"/> with no contents.
+		/// </summary>
+		public ExtendedHeader ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ExtendedHeader" /> by reading the raw contents from
+		///    a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    extended header structure.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value indicating the ID3v2 version.
+		/// </param>
+		public ExtendedHeader (ByteVector data, byte version)
+		{
+			Parse (data, version);
+		}
+		
+		/// <summary>
+		///    Gets the size of the data on disk in bytes.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    data on disk.
+		/// </value>
+		public uint Size {
+			get {return size;}
+		}
+		
+		/// <summary>
+		///    Populates the current instance with the contents of the
+		///    raw ID3v2 frame.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    extended header structure.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value indicating the ID3v2 version.
+		/// </param>
+		protected void Parse (ByteVector data, byte version)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			size = (version == 3 ? 4u : 0u) + SynchData.ToUInt (data.Mid (0, 4));
+		}
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="ExtendedHeader" /> object identical to
+		///    the current instance.
+		/// </returns>
+		public ExtendedHeader Clone ()
+		{
+			ExtendedHeader header = new ExtendedHeader ();
+			header.size = size;
+			return header;
+		}
+		
+		object ICloneable.Clone ()
+		{
+			return Clone ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Footer.cs b/lib/TagLib/TagLib/Id3v2/Footer.cs
new file mode 100644
index 0000000..ad34299
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Footer.cs
@@ -0,0 +1,295 @@
+//
+// Footer.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v2header.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Id3v2
+{
+	
+	/// <summary>
+	///    This structure provides a representation of an ID3v2 tag footer
+	///    which can be read from and written to disk.
+	/// </summary>
+	public struct Footer
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the tag's major version.
+		/// </summary>
+		private byte major_version;
+		
+		/// <summary>
+		///    Contains the tag's version revision.
+		/// </summary>
+		private byte revision_number;
+		
+		/// <summary>
+		///    Contains tag's flags.
+		/// </summary>
+		private HeaderFlags flags;
+		
+		/// <summary>
+		///    Contains the tag size.
+		/// </summary>
+		private uint tag_size;
+		
+#endregion
+		
+		
+		
+#region Public Fields
+		
+		/// <summary>
+		///    The size of a ID3v2 footer.
+		/// </summary>
+		public const uint Size = 10;
+		
+		/// <summary>
+		///    The identifier used to recognize a ID3v2 footer.
+		/// </summary>
+		/// <value>
+		///    "3DI"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "3DI";
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Footer" /> by reading it from raw footer data.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data to build the new instance from.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> is smaller than <see
+		///    cref="Size" />, does not begin with <see
+		///    cref="FileIdentifier" />, contains invalid flag data,
+		///    or contains invalid size data.
+		/// </exception>
+		public Footer (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"Provided data is smaller than object size.");
+			
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"Provided data does not start with the file identifier");
+			
+			major_version = data [3];
+			revision_number = data [4];
+			flags = (HeaderFlags) data [5];
+			
+			if (major_version == 2 && ((int) flags & 127) != 0)
+				throw new CorruptFileException (
+					"Invalid flags set on version 2 tag.");
+			
+			if (major_version == 3 && ((int) flags & 15) != 0)
+				throw new CorruptFileException (
+					"Invalid flags set on version 3 tag.");
+			
+			if (major_version == 4 && ((int) flags & 7) != 0)
+				throw new CorruptFileException (
+					"Invalid flags set on version 4 tag.");
+			
+			for (int i = 6; i < 10; i ++)
+				if (data [i] >= 128)
+					throw new CorruptFileException (
+						"One of the bytes in the header was greater than the allowed 128.");
+			
+			tag_size = SynchData.ToUInt (data.Mid (6, 4));
+		}
+		
+		/// <summary>
+		///    Constructs and intializes a new instance of <see
+		///    cref="Footer" /> by reading in the contents of the header
+		///    object used for the same tag.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="Header" /> object to base the new instance
+		///    off of.
+		/// </param>
+		public Footer (Header header)
+		{
+			major_version = header.MajorVersion;
+			revision_number = header.RevisionNumber;
+			flags = header.Flags | HeaderFlags.FooterPresent;
+			tag_size = header.TagSize;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the major version of the tag described by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value specifying the ID3v2 version
+		///    of tag described by the current instance.
+		/// </value>
+		/// <remarks>
+		///    When the version is set, unsupported header flags will
+		///    automatically be removed from the tag.
+		/// </remarks>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="value" /> is not 4.
+		/// </exception>
+		public byte MajorVersion {
+			get {
+				return major_version == 0 ? Tag.DefaultVersion :
+					major_version;
+			}
+			set {
+				if (value != 4)
+					throw new ArgumentException (
+						"Version unsupported.");
+				
+				major_version = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the version revision number of the tag
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value containing the version
+		///    revision number of the tag represented by the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    This value should always be zeroed. A non-zero value
+		///    indicates an experimental or new version of the format
+		///    which may not be completely understood by the current
+		///    implementation. Some software may refuse to read tags
+		///    with a non-zero value.
+		/// </remarks>
+		public byte RevisionNumber {
+			get {return revision_number;}
+			set {revision_number = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the flags applied to the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="HeaderFlags" /> value
+		///    containing the flags applied to the current instance.
+		/// </value>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="value" /> contains a flag not supported
+		///    by the the ID3v2 version of the current instance.
+		/// </exception>
+		public HeaderFlags Flags {
+			get {return flags;}
+			set {
+				if (0 != (value & (HeaderFlags.ExtendedHeader |
+					HeaderFlags.ExperimentalIndicator)) &&
+					MajorVersion < 3)
+					throw new ArgumentException (
+						"Feature only supported in version 2.3+",
+						"value");
+				
+				if (0 != (value & HeaderFlags.FooterPresent) &&
+					MajorVersion < 3)
+					throw new ArgumentException (
+						"Feature only supported in version 2.4+",
+						"value");
+				
+				flags = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the size of the tag described by the
+		///    current instance, minus the header and footer.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    tag described by the current instance.
+		/// </value>
+		public uint TagSize {
+			get {return tag_size;}
+			set {tag_size = value;}
+		}
+		
+		/// <summary>
+		///    Gets the complete size of the tag described by the
+		///    current instance, including the header and footer.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the complete size
+		///    of the tag described by the current instance.
+		/// </value>
+		public uint CompleteTagSize {
+			get {return TagSize + Header.Size + Size;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ID3v2 header.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered header.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector v = new ByteVector ();
+			v.Add (FileIdentifier);
+			v.Add (MajorVersion);
+			v.Add (RevisionNumber);
+			v.Add ((byte)flags);
+			v.Add (SynchData.FromUInt (TagSize));
+			return v;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frame.cs b/lib/TagLib/TagLib/Id3v2/Frame.cs
new file mode 100644
index 0000000..e5e047f
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frame.cs
@@ -0,0 +1,539 @@
+//
+// Frame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v2frame.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This abstract class provides a basic framework for representing
+	///    ID3v2.4 frames.
+	/// </summary>
+	public abstract class Frame : ICloneable
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the frame's header.
+		/// </summary>
+		private FrameHeader header;
+		
+		/// <summary>
+		///    Contains the frame's grouping ID.
+		/// </summary>
+		private byte group_id;
+		
+		/// <summary>
+		///    Contains the frame's encryption ID.
+		/// </summary>
+		private byte encryption_id;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Frame" /> by reading the raw header encoded in the
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier or header data to use for the new instance.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value indicating the ID3v2 version
+		///    which <paramref name="data" /> is encoded in.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="data" /> does not contain a complete
+		///    identifier.
+		/// </exception>
+		protected Frame (ByteVector data, byte version)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < ((version < 3) ? 3 : 4))
+				throw new ArgumentException (
+					"Data contains an incomplete identifier.",
+					"data");
+			
+			header = new FrameHeader (data, version);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Frame" /> with a specified header.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> value containing the header
+		///    to use for the new instance.
+		/// </param>
+		protected Frame (FrameHeader header)
+		{
+			this.header = header;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the frame ID for the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ReadOnlyByteVector" /> object containing the
+		///    four-byte ID3v2.4 frame header for the current instance.
+		/// </value>
+		public ReadOnlyByteVector FrameId {
+			get {return header.FrameId;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the current instance as it was last
+		///    stored on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    current instance as it was last stored on disk.
+		/// </value>
+		public uint Size {
+			get {return header.FrameSize;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the frame flags applied to the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="FrameFlags" /> value
+		///    containing the frame flags applied to the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    If the value includes either <see
+		///    cref="FrameFlags.Encryption" /> or <see
+		///    cref="FrameFlags.Compression" />, <see cref="Render" />
+		///    will throw a <see cref="NotImplementedException" />.
+		/// </remarks>
+		public FrameFlags Flags {
+			get {return header.Flags;}
+			set {header.Flags = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping ID applied to the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="short" /> value containing the grouping
+		///    identifier for the current instance, or -1 if not set.
+		/// </value>
+		/// <remarks>
+		///    Grouping identifiers can be between 0 and 255. Setting
+		///    any other value will unset the grouping identity and set
+		///    the value to -1.
+		/// </remarks>
+		public short GroupId {
+			get {
+				return (Flags & FrameFlags.GroupingIdentity)
+					!= 0 ? group_id : (short) -1;
+			}
+			set {
+				if (value >= 0x00 && value <= 0xFF) {
+					group_id = (byte) value;
+					Flags |= FrameFlags.GroupingIdentity;
+				} else {
+					Flags &= ~FrameFlags.GroupingIdentity;
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the encryption ID applied to the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="short" /> value containing the encryption
+		///    identifier for the current instance, or -1 if not set.
+		/// </value>
+		/// <remarks>
+		///    <para>Encryption identifiers can be between 0 and 255.
+		///    Setting any other value will unset the grouping identity
+		///    and set the value to -1.</para>
+		///    <para>If set, <see cref="Render" /> will throw a <see
+		///    cref="NotImplementedException" />.</para>
+		/// </remarks>
+		public short EncryptionId {
+			get {
+				return (Flags & FrameFlags.Encryption) != 0 ?
+					encryption_id : (short) -1;
+			}
+			set {
+				if (value >= 0x00 && value <= 0xFF) {
+					encryption_id = (byte) value;
+					Flags |= FrameFlags.Encryption;
+				} else {
+					Flags &= ~FrameFlags.Encryption;
+				}
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance, encoded in a specified
+		///    ID3v2 version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> value specifying the version of
+		///    ID3v2 to use when encoding the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		/// <exception cref="NotImplementedException">
+		///    The current instance uses some feature that cannot be
+		///    implemented in the specified ID3v2 version, or uses a
+		///    feature, such as encryption or compression, which is not
+		///    yet implemented in the library.
+		/// </exception>
+		public virtual ByteVector Render (byte version)
+		{
+			// Remove flags that are not supported by older versions
+			// of ID3v2.
+			if (version < 4)
+				Flags &= ~(FrameFlags.DataLengthIndicator |
+					FrameFlags.Unsynchronisation);
+			
+			if (version < 3)
+				Flags &= ~(FrameFlags.Compression |
+					FrameFlags.Encryption |
+					FrameFlags.FileAlterPreservation |
+					FrameFlags.GroupingIdentity |
+					FrameFlags.ReadOnly |
+					FrameFlags.TagAlterPreservation);
+			
+			ByteVector field_data = RenderFields (version);
+			
+			// If we don't have any content, don't render anything.
+			// This will cause the frame to not be rendered.
+			if (field_data.Count == 0)
+				return new ByteVector ();
+			
+			ByteVector front_data = new ByteVector ();
+			
+			if ((Flags & (FrameFlags.Compression |
+				FrameFlags.DataLengthIndicator)) != 0)
+				front_data.Add (ByteVector.FromUInt ((uint)
+					field_data.Count));
+			
+			if ((Flags & FrameFlags.GroupingIdentity) != 0)
+				front_data.Add (group_id);
+			
+			if ((Flags & FrameFlags.Encryption) != 0)
+				front_data.Add (encryption_id);
+			
+			// FIXME: Implement compression.
+			if ((Flags & FrameFlags.Compression) != 0)
+				throw new NotImplementedException (
+					"Compression not yet supported");
+			
+			// FIXME: Implement encryption.
+			if ((Flags & FrameFlags.Encryption) != 0)
+				throw new NotImplementedException (
+					"Encryption not yet supported");
+			
+			if ((Flags & FrameFlags.Unsynchronisation) != 0)
+				SynchData.UnsynchByteVector (field_data);
+			
+			if (front_data.Count > 0)
+				field_data.Insert (0, front_data);
+			
+			header.FrameSize = (uint) field_data.Count;
+			ByteVector header_data = header.Render (version);
+			header_data.Add (field_data);
+			
+			return header_data;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets the text delimiter for a specified encoding.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType" /> value specifying the encoding
+		///    to get the delimiter for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    delimiter for the specified encoding.
+		/// </returns>
+		[Obsolete("Use ByteVector.TextDelimiter.")]
+		public static ByteVector TextDelimiter (StringType type)
+		{
+			return ByteVector.TextDelimiter (type);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Converts an encoding to be a supported encoding for a
+		///    specified tag version.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="StringType" /> value containing the original
+		///    encoding.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the ID3v2 version
+		///    to be encoded for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="StringType" /> value containing the correct
+		///    encoding to use, based on <see
+		///    cref="Tag.ForceDefaultEncoding" /> and what is supported
+		///    by <paramref name="version" />.
+		/// </returns>
+		protected static StringType CorrectEncoding (StringType type,
+		                                             byte version)
+		{
+			if (Tag.ForceDefaultEncoding)
+				type = Tag.DefaultEncoding;
+			
+			return (version < 4 && type == StringType.UTF8) ?
+				StringType.UTF16 : type;
+		}
+		
+		/// <summary>
+		///    Populates the current instance by reading the raw frame
+		///    from disk, optionally reading the header.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    ID3v2 frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value containing the offset in
+		///    <paramref name="data" /> at which the frame begins.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the ID3v2 version
+		///    of the raw frame contained in <paramref name="data" />.
+		/// </param>
+		/// <param name="readHeader">
+		///    A <see cref="bool" /> value indicating whether or not to
+		///    read the header into current instance.
+		/// </param>
+		protected void SetData (ByteVector data, int offset,
+		                        byte version, bool readHeader)
+		{
+			if (readHeader)
+				header = new FrameHeader (data, version);
+			ParseFields (FieldData (data, offset, version),
+				version);
+		}
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected abstract void ParseFields (ByteVector data,
+		                                     byte version);
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected abstract ByteVector RenderFields (byte version);
+		
+		/// <summary>
+		///    Extracts the field data from the raw data portion of an
+		///    ID3v2 frame.
+		/// </summary>
+		/// <param name="frameData">
+		///    A <see cref="ByteVector" /> object containing fraw frame
+		///    data.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value containing the index at which
+		///    the data is contained.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the ID3v2 version
+		///    of the data.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </returns>
+		/// <remarks>
+		///    This method is necessary for extracting extra data
+		///    prepended to the frame such as the grouping ID.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="frameData" /> is <see langword="null" />.
+		/// </exception>
+		protected ByteVector FieldData (ByteVector frameData,
+		                                int offset, byte version)
+		{
+			if (frameData == null)
+				throw new ArgumentNullException ("frameData");
+			
+			int data_offset = offset + (int) FrameHeader.Size (version);
+			int data_length = (int) Size;
+
+			if ((Flags & (FrameFlags.Compression |
+				FrameFlags.DataLengthIndicator)) != 0) {
+				data_offset += 4;
+				data_length -= 4;
+			}
+			
+			if ((Flags & FrameFlags.GroupingIdentity) != 0) {
+				if (frameData.Count >= data_offset)
+					throw new TagLib.CorruptFileException (
+						"Frame data incomplete.");
+				group_id = frameData [data_offset++];
+				data_length--;
+			}
+			
+			if ((Flags & FrameFlags.Encryption) != 0) {
+				if (frameData.Count >= data_offset)
+					throw new TagLib.CorruptFileException (
+						"Frame data incomplete.");
+				encryption_id = frameData [data_offset++];
+				data_length--;
+			}
+
+			data_length = Math.Min (data_length, frameData.Count - data_offset);
+			if (data_length < 0 )
+				throw new CorruptFileException (
+					"Frame size less than zero.");
+			
+			ByteVector data = frameData.Mid (data_offset,
+				data_length);
+
+            if ((Flags & FrameFlags.Unsynchronisation) != 0) {
+                int before_length = data.Count;
+				SynchData.ResynchByteVector (data);
+                data_length -= (data.Count - before_length);
+            }
+			
+			// FIXME: Implement encryption.
+			if ((Flags & FrameFlags.Encryption) != 0)
+				throw new NotImplementedException ();
+			
+			// FIXME: Implement compression.
+			if ((Flags & FrameFlags.Compression) != 0)
+				throw new NotImplementedException ();
+			/*
+			if(d->header->compression()) {
+				ByteVector data(frameDataLength);
+				uLongf uLongTmp = frameDataLength;
+				::uncompress((Bytef *) data.data(),
+				(uLongf *) &uLongTmp,
+				(Bytef *) frameData.data() + frameDataOffset,
+				size());
+				return data;
+			}
+			*/
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		/// <remarks>
+		///    This method is implemented by rendering the current
+		///    instance as an ID3v2.4 frame and using <see
+		///    cref="FrameFactory.CreateFrame" /> to create a new
+		///    frame. As such, this method should be overridden by
+		///    child classes.
+		/// </remarks>
+		public virtual Frame Clone ()
+		{
+			int index = 0;
+			return FrameFactory.CreateFrame (Render (4), ref index,
+				4, false);
+		}
+		
+		object ICloneable.Clone ()
+		{
+			return Clone ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/FrameFactory.cs b/lib/TagLib/TagLib/Id3v2/FrameFactory.cs
new file mode 100644
index 0000000..7ab744c
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/FrameFactory.cs
@@ -0,0 +1,283 @@
+//
+// FrameFactory.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v2framefactory.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System.Collections.Generic;
+ 
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    This static class performs the necessary operations to determine
+	///    and create the correct child class of <see cref="Frame" /> for a
+	///    given raw ID3v2 frame.
+	/// </summary>
+	/// <remarks>
+	///    By default, <see cref="FrameFactory" /> will only load frames
+	///    contained in the library. To add additional frames to the
+	///    process, register a frame creator with <see
+	///    cref="AddFrameCreator" />.
+	/// </remarks>
+	public static class FrameFactory
+	{
+		/// <summary>
+		///    Creates a frame from a specified block of data, or
+		///    returns <see langword="null" /> if unsupported.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing a raw ID3v2
+		///    frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the offset in
+		///    <paramref name="data"/> at which the frame data begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> object for the frame
+		///    contained in <paramref name="data" />.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> specifying the version of ID3v2 the
+		///    raw frame data is stored in.
+		/// </param>
+		/// <returns>
+		///     A <see cref="Frame" /> object if the method was able to
+		///     match and create one. Otherwise <see langword="null" />.
+		/// </returns>
+		/// <remarks>
+		///    <para>Frame creators are used to provide access or
+		///    support for items that are left out of TagLib#.</para>
+		/// </remarks>
+		/// <example>
+		///    <code lang="C#">
+		/// public Frame Creator (TagLib.ByteVector data, TagLib.Id3v2.FrameHeader header)
+		/// {
+		/// 	if (header.FrameId == "RVRB")
+		/// 		return new ReverbFrame (data, header);
+		/// 	else
+		/// 		return null;
+		/// }
+		/// ...
+		/// TagLib.Id3v2.FrameFactor.AddFrameCreator (ReverbFrame.Creator);
+		///   </code>
+		/// </example>
+		/// <seealso cref="AddFrameCreator" />
+		public delegate Frame FrameCreator (ByteVector data, int offset,
+		                                    FrameHeader header,
+		                                    byte version);
+		
+		/// <summary>
+		///    Contains registered frame creators.
+		/// </summary>
+		private static List<FrameCreator> frame_creators =
+			new List<FrameCreator> ();
+		
+		/// <summary>
+		///    Creates a <see cref="Frame" /> object by reading it from
+		///    raw ID3v2 frame data.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing a raw ID3v2
+		///    frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value reference specifying at what
+		///    index in <paramref name="data" /> at which the frame
+		///    begins. After reading, it contains the offset of the next
+		///    frame to be read.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value specifying the ID3v2 version
+		///    the frame in <paramref name="data"/> is encoded in.
+		/// </param>
+		/// <param name="alreadyUnsynched">
+		///    A <see cref="bool" /> value specifying whether the entire
+		///    tag has already been unsynchronized.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Frame" /> object read from the data, or <see
+		///    langword="null" /> if none is found.
+		/// </returns>
+		/// <exception cref="System.NotImplementedException">
+		///    The frame contained in the raw data could not be
+		///    converted to ID3v2 or uses encryption or compression.
+		/// </exception>
+		public static Frame CreateFrame (ByteVector data,
+		                                 ref int offset, byte version, bool alreadyUnsynched)
+		{
+			int position = offset;
+			
+			FrameHeader header = new FrameHeader (data.Mid (position,
+				(int) FrameHeader.Size (version)), version);
+			
+			offset += (int) (header.FrameSize + FrameHeader.Size (
+				version));
+			
+			if (header.FrameId == null)
+				throw new System.NotImplementedException ();
+			
+			foreach (byte b in header.FrameId) {
+				char c = (char) b;
+					if ((c < 'A' || c > 'Z') &&
+						(c < '1' || c > '9'))
+						return null;
+			}
+
+            if (alreadyUnsynched) {
+                // Mark the frame as not Unsynchronozed because the entire
+                // tag has already been Unsynchronized
+                header.Flags &= ~FrameFlags.Unsynchronisation;
+            }
+			
+			// Windows Media Player may create zero byte frames.
+			// Just send them off as unknown and delete them.
+			if (header.FrameSize == 0) {
+				header.Flags |= FrameFlags.TagAlterPreservation;
+				return new UnknownFrame (data, position, header,
+					version);
+			}
+			
+			// TODO: Support Compression.
+			if ((header.Flags & FrameFlags.Compression) != 0)
+				throw new System.NotImplementedException ();
+			
+			// TODO: Support Encryption.
+			if ((header.Flags & FrameFlags.Encryption) != 0)
+				throw new System.NotImplementedException ();
+			
+			foreach (FrameCreator creator in frame_creators) {
+				Frame frame = creator (data, position, header,
+					version);
+				
+				if (frame != null)
+					return frame;
+			}
+			
+			// 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 (header.FrameId == FrameType.TXXX)
+				return new UserTextInformationFrame (data,
+					position, header, version);
+			
+			if (header.FrameId [0] == (byte) 'T')
+				return new TextInformationFrame (data, position,
+					header, version);
+			
+			// Unique File Identifier (frames 4.1)
+			if (header.FrameId == FrameType.UFID)
+				return new UniqueFileIdentifierFrame (data,
+					position, header, version);
+			
+			// Music CD Identifier (frames 4.5)
+			if (header.FrameId == FrameType.MCDI)
+				return new MusicCdIdentifierFrame (data,
+					position, header, version);
+			
+			// Unsynchronized Lyrics (frames 4.8)
+			if (header.FrameId == FrameType.USLT)
+				return new UnsynchronisedLyricsFrame (data,
+					position, header, version);
+			
+			// Synchronized Lyrics (frames 4.9)
+			if (header.FrameId == FrameType.SYLT)
+				return new SynchronisedLyricsFrame (data,
+					position, header, version);
+			
+			// Comments (frames 4.10)
+			if (header.FrameId == FrameType.COMM)
+				return new CommentsFrame (data, position,
+					header, version);
+			
+			// Relative Volume Adjustment (frames 4.11)
+			if (header.FrameId == FrameType.RVA2)
+				return new RelativeVolumeFrame (data, position,
+					header, version);
+			
+			// Attached Picture (frames 4.14)
+			if (header.FrameId == FrameType.APIC)
+				return new AttachedPictureFrame (data, position,
+					header, version);
+			
+			// General Encapsulated Object (frames 4.15)
+			if(header.FrameId == FrameType.GEOB)
+				return new GeneralEncapsulatedObjectFrame (data,
+					position, header, version);
+			
+			// Play Count (frames 4.16)
+			if(header.FrameId == FrameType.PCNT)
+				return new PlayCountFrame (data, position,
+					header, version);
+			
+			// Play Count (frames 4.17)
+			if(header.FrameId == FrameType.POPM)
+				return new PopularimeterFrame (data, position,
+					header, version);
+			
+			// Terms of Use (frames 4.22)
+			if(header.FrameId == FrameType.USER)
+				return new TermsOfUseFrame (data, position,
+					header, version);
+			
+			// Private (frames 4.27)
+			if (header.FrameId == FrameType.PRIV)
+				return new PrivateFrame (data, position, header,
+					version);
+			
+			return new UnknownFrame (data, position, header,
+				version);
+		}
+
+		/// <summary>
+		///    Adds a curstom frame creator to try before using standard
+		///    frame creation methods.
+		/// </summary>
+		/// <param name="creator">
+		///    A <see cref="FrameCreator" /> delegate to be used by the
+		///    frame factory.
+		/// </param>
+		/// <remarks>
+		///    Frame creators are used before standard methods so custom
+		///    checking can be used and new formats can be added. They
+		///    are executed in the reverse order in which they are
+		///    added.
+		/// </remarks>
+		/// <exception cref="System.ArgumentNullException">
+		///    <paramref name="creator" /> is <see langword="null" />.
+		/// </exception>
+		public static void AddFrameCreator (FrameCreator creator)
+		{
+			if (creator == null)
+				throw new System.ArgumentNullException (
+					"creator");
+			
+			frame_creators.Insert (0, creator);
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/FrameHeader.cs b/lib/TagLib/TagLib/Id3v2/FrameHeader.cs
new file mode 100644
index 0000000..571d362
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/FrameHeader.cs
@@ -0,0 +1,489 @@
+//
+// FrameHeader.cs:
+//
+// Authors:
+//   Brian Nickel (brian nickel gmail com)
+//   Gabriel BUrt (gabriel burt gmail com)
+//
+// Original Source:
+//   id3v2frame.cpp from TagLib
+//
+// Copyright (C) 2010 Novell, Inc.
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    Indicates the flags applied to a <see cref="FrameHeader" />
+	///    object.
+	/// </summary>
+	[Flags]
+	public enum FrameFlags : ushort
+	{
+		/// <summary>
+		///    The header contains no flags.
+		/// </summary>
+		None = 0,
+		
+		/// <summary>
+		///    Indicates that the frame is to be deleted if the tag is
+		///    altered.
+		/// </summary>
+		TagAlterPreservation = 0x4000,
+		
+		/// <summary>
+		///    Indicates that the frame is to be deleted if the file is
+		///    altered.
+		/// </summary>
+		FileAlterPreservation = 0x2000,
+		
+		/// <summary>
+		///    Indicates that the frame is read-only and should not be
+		///    altered.
+		/// </summary>
+		ReadOnly = 0x1000,
+		
+		/// <summary>
+		///    Indicates that the frame has a grouping identity.
+		/// </summary>
+		GroupingIdentity = 0x0040,
+		
+		/// <summary>
+		///    Indicates that the frame data is compressed.
+		/// </summary>
+		Compression = 0x0008,
+		
+		/// <summary>
+		///    Indicates that the frame data is encrypted.
+		/// </summary>
+		Encryption = 0x0004,
+		
+		/// <summary>
+		///    Indicates that the frame data has been unsynchronized.
+		/// </summary>
+		Unsynchronisation = 0x0002,
+		
+		/// <summary>
+		///    Indicates that the frame has a data length indicator.
+		/// </summary>
+		DataLengthIndicator = 0x0001
+	}
+	
+	/// <summary>
+	///    This structure provides a representation of an ID3v2 frame header
+	///    which can be read from and written to disk.
+	/// </summary>
+	public struct FrameHeader
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains frame's ID.
+		/// </summary>
+		private ReadOnlyByteVector frame_id;
+		
+		/// <summary>
+		///    Contains frame's size.
+		/// </summary>
+		private uint frame_size;
+		
+		/// <summary>
+		///    Contains frame's flags.
+		/// </summary>
+		private FrameFlags flags;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="FrameHeader" /> by reading it from raw header data
+		///    of a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data to build the new instance from.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the ID3v2 version
+		///    with which the data in <paramref name="data" /> was
+		///    encoded.
+		/// </param>
+		/// <remarks>
+		///    If the data size is smaller than the size of a full
+		///    header, the data is just treated as a frame identifier 
+		///    and the remaining values are zeroed.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> is smaller than the size of a
+		///    frame identifier or <paramref name="version" /> is less
+		///    than 2 or more than 4.
+		/// </exception>
+		public FrameHeader (ByteVector data, byte version)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			flags = 0;
+			frame_size = 0;
+			
+			if (version < 2 || version > 4)
+				throw new CorruptFileException (
+					"Unsupported tag version.");
+			
+			if (data.Count < (version == 2 ? 3 : 4))
+				throw new CorruptFileException (
+					"Data must contain at least a frame ID.");
+			
+			switch (version)
+			{
+			case 2:
+				// Set the frame ID -- the first three bytes
+				frame_id = ConvertId (data.Mid (0, 3), version,
+					false);
+			
+				// If the full header information was not passed
+				// in, do not continue to the steps to parse the
+				// frame size and flags.
+				if (data.Count < 6)
+					return;
+				
+				frame_size = data.Mid (3, 3).ToUInt ();
+				return;
+			
+			case 3:
+				// Set the frame ID -- the first four bytes
+				frame_id = ConvertId (data.Mid (0, 4), version,
+					false);
+				
+				// If the full header information was not passed
+				// in, do not continue to the steps to parse the
+				// frame size and flags.
+				if (data.Count < 10)
+					return;
+				
+				// Store the flags internally as version 2.4.
+				frame_size = data.Mid (4, 4).ToUInt ();
+				flags = (FrameFlags) (
+					((data [8] << 7) & 0x7000) |
+					((data [9] >> 4) & 0x000C) |
+					((data [9] << 1) & 0x0040));
+				
+				return;
+			
+			case 4:
+				// Set the frame ID -- the first four bytes
+				frame_id = new ReadOnlyByteVector (
+					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.Count < 10)
+					return;
+				
+				frame_size = SynchData.ToUInt (data.Mid (4, 4));
+				flags = (FrameFlags) data.Mid (8, 2).ToUShort ();
+				
+				return;
+			
+			default:
+				throw new CorruptFileException (
+					"Unsupported tag version.");
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the identifier of the frame described by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ReadOnlyByteVector" /> object containing the
+		///    identifier of the frame described by the current
+		///    instance.
+		/// </value>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="value" /> is <see langword="null" />.
+		/// </exception>
+		public ReadOnlyByteVector FrameId {
+			get {return frame_id;}
+			set {
+				if (value == null)
+					throw new ArgumentNullException (
+						"value");
+				
+				frame_id = value.Count == 4 ?
+					value : new ReadOnlyByteVector (
+						value.Mid (0, 4));
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the size of the frame described by the
+		///    current instance, minus the header.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    frame described by the current instance.
+		/// </value>
+		public uint FrameSize {
+			get {return frame_size;}
+			set {frame_size = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the flags applied to the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="HeaderFlags" /> value
+		///    containing the flags applied to the current instance.
+		/// </value>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="value" /> contains a either compression
+		///    or encryption, neither of which are supported by the
+		///    library.
+		/// </exception>
+		public FrameFlags Flags {
+			get {return flags;}
+			set {
+				if ((value & (FrameFlags.Compression |
+					FrameFlags.Encryption)) != 0)
+					throw new ArgumentException (
+						"Encryption and compression are not supported.",
+						"value");
+				
+				flags = value;
+			}
+		}
+		
+		#endregion
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance, encoded in a specified
+		///    ID3v2 version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> value specifying the version of
+		///    ID3v2 to use when encoding the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		/// <exception cref="NotImplementedException">
+		///    The version specified in the current instance is
+		///    unsupported.
+		/// </exception>
+		public ByteVector Render (byte version) {
+			ByteVector data = new ByteVector ();
+			ByteVector id = ConvertId (frame_id, version, true);
+			
+			if (id == null)
+				throw new NotImplementedException ();
+			
+			switch (version)
+			{
+			case 2:
+				data.Add (id);
+				data.Add (ByteVector.FromUInt (frame_size)
+					.Mid (1, 3));
+				
+				return data;
+			
+			case 3:
+				ushort new_flags = (ushort) (
+					(((ushort)flags << 1) & 0xE000) |
+					(((ushort)flags << 4) & 0x00C0) |
+					(((ushort)flags >> 1) & 0x0020));
+				
+				data.Add (id);
+				data.Add (ByteVector.FromUInt (frame_size));
+				data.Add (ByteVector.FromUShort (new_flags));
+				
+				return data;
+			
+			case 4:
+				data.Add (id);
+				data.Add (SynchData.FromUInt (frame_size));
+				data.Add (ByteVector.FromUShort ((ushort) flags));
+				
+				return data;
+			
+			default:
+				throw new NotImplementedException (
+					"Unsupported tag version.");
+			}
+		}
+		
+		/// <summary>
+		///    Gets the size of a header for a specified ID3v2 version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> value specifying the version of
+		///    ID3v2 to get the size for.
+		/// </param>
+		public static uint Size (byte version)
+		{
+			return (uint) (version < 3 ? 6 : 10);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		private static ReadOnlyByteVector ConvertId (ByteVector id,
+		                                             byte version,
+		                                             bool toVersion)
+		{
+			if (version >= 4) {
+				ReadOnlyByteVector outid =
+					id as ReadOnlyByteVector;
+				
+				return outid != null ?
+					outid : new ReadOnlyByteVector (id);
+			}
+			
+			if (id == null || version < 2)
+				return null;
+			
+			if (!toVersion && (id == FrameType.EQUA ||
+				id == FrameType.RVAD || id == FrameType.TRDA ||
+				id == FrameType.TSIZ))
+				return null;
+			
+			if (version == 2)
+				for (int i = 0; i < version2_frames.GetLength (0); i ++) {
+					if (!version2_frames [i,
+						toVersion ? 1 : 0].Equals (id))
+						continue;
+					
+					return version2_frames [i,
+						toVersion ? 0 : 1];
+				}
+			
+			if (version == 3)
+				for (int i = 0; i < version3_frames.GetLength (0); i ++) {
+					if (!version3_frames [i,
+						toVersion ? 1 : 0].Equals (id))
+						continue;
+					
+					return version3_frames [i,
+						toVersion ? 0 : 1];
+				}
+			
+			if ((id.Count != 4 && version > 2) ||
+				(id.Count != 3 && version == 2))
+				return null;
+			
+			return id is ReadOnlyByteVector ?
+				id as ReadOnlyByteVector :
+				new ReadOnlyByteVector (id);
+		}
+		
+		private static readonly ReadOnlyByteVector [,] version2_frames =
+			new ReadOnlyByteVector [59,2] {
+				{ "BUF", "RBUF" },
+				{ "CNT", "PCNT" },
+				{ "COM", "COMM" },
+				{ "CRA", "AENC" },
+				{ "ETC", "ETCO" },
+				{ "GEO", "GEOB" },
+				{ "IPL", "TIPL" },
+				{ "MCI", "MCDI" },
+				{ "MLL", "MLLT" },
+				{ "PIC", "APIC" },
+				{ "POP", "POPM" },
+				{ "REV", "RVRB" },
+				{ "SLT", "SYLT" },
+				{ "STC", "SYTC" },
+				{ "TAL", "TALB" },
+				{ "TBP", "TBPM" },
+				{ "TCM", "TCOM" },
+				{ "TCO", "TCON" },
+				{ "TCP", "TCMP" },
+				{ "TCR", "TCOP" },
+				{ "TDA", "TDAT" },
+				{ "TIM", "TIME" },
+				{ "TDY", "TDLY" },
+				{ "TEN", "TENC" },
+				{ "TFT", "TFLT" },
+				{ "TKE", "TKEY" },
+				{ "TLA", "TLAN" },
+				{ "TLE", "TLEN" },
+				{ "TMT", "TMED" },
+				{ "TOA", "TOAL" },
+				{ "TOF", "TOFN" },
+				{ "TOL", "TOLY" },
+				{ "TOR", "TDOR" },
+				{ "TOT", "TOAL" },
+				{ "TP1", "TPE1" },
+				{ "TP2", "TPE2" },
+				{ "TP3", "TPE3" },
+				{ "TP4", "TPE4" },
+				{ "TPA", "TPOS" },
+				{ "TPB", "TPUB" },
+				{ "TRC", "TSRC" },
+				{ "TRK", "TRCK" },
+				{ "TSS", "TSSE" },
+				{ "TT1", "TIT1" },
+				{ "TT2", "TIT2" },
+				{ "TT3", "TIT3" },
+				{ "TXT", "TOLY" },
+				{ "TXX", "TXXX" },
+				{ "TYE", "TDRC" },
+				{ "UFI", "UFID" },
+				{ "ULT", "USLT" },
+				{ "WAF", "WOAF" },
+				{ "WAR", "WOAR" },
+				{ "WAS", "WOAS" },
+				{ "WCM", "WCOM" },
+				{ "WCP", "WCOP" },
+				{ "WPB", "WPUB" },
+				{ "WXX", "WXXX" },
+				{ "XRV", "RVA2" }
+			};
+		
+		private static readonly ReadOnlyByteVector [,] version3_frames =
+			new ReadOnlyByteVector [3,2] {
+				{ "TORY", "TDOR" },
+				{ "TYER", "TDRC" },
+				{ "XRVA", "RVA2" }
+
+			};
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/FrameTypes.cs b/lib/TagLib/TagLib/Id3v2/FrameTypes.cs
new file mode 100644
index 0000000..dc2acfa
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/FrameTypes.cs
@@ -0,0 +1,85 @@
+//
+// FrameTypes.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Id3v2 {
+       /// <summary>
+       ///    <see cref="FrameType" /> provides references to different frame
+       ///    types used by the library.
+       /// </summary>
+       /// <remarks>
+       ///    <para>This class is used to severely reduce the number of times
+       ///    these types are created in <see cref="TagLib.Id3v2.Tag" />,
+       ///    greatly improving the speed at which warm files are read. It is,
+       ///    however, not necessary for external users to use this class. While
+       ///    the library may use <c>GetTextAsString (FrameType.TIT2);</c> an
+       ///    external user could use <c>tag.GetTextAsString ("TIT2");</c> with
+       ///    the same result.</para>
+       /// </remarks>
+	internal static class FrameType {
+		public static readonly ReadOnlyByteVector APIC = "APIC";
+		public static readonly ReadOnlyByteVector COMM = "COMM";
+		public static readonly ReadOnlyByteVector EQUA = "EQUA";
+		public static readonly ReadOnlyByteVector GEOB = "GEOB";
+		public static readonly ReadOnlyByteVector MCDI = "MCDI";
+		public static readonly ReadOnlyByteVector PCNT = "PCNT";
+		public static readonly ReadOnlyByteVector POPM = "POPM";
+		public static readonly ReadOnlyByteVector PRIV = "PRIV";
+		public static readonly ReadOnlyByteVector RVA2 = "RVA2";
+		public static readonly ReadOnlyByteVector RVAD = "RVAD";
+		public static readonly ReadOnlyByteVector SYLT = "SYLT";
+		public static readonly ReadOnlyByteVector TALB = "TALB";
+		public static readonly ReadOnlyByteVector TBPM = "TBPM";
+		public static readonly ReadOnlyByteVector TCOM = "TCOM";
+		public static readonly ReadOnlyByteVector TCON = "TCON";
+		public static readonly ReadOnlyByteVector TCOP = "TCOP";
+		public static readonly ReadOnlyByteVector TCMP = "TCMP";
+		public static readonly ReadOnlyByteVector TDRC = "TDRC";
+		public static readonly ReadOnlyByteVector TDAT = "TDAT";
+		public static readonly ReadOnlyByteVector TEXT = "TEXT";
+		public static readonly ReadOnlyByteVector TIT1 = "TIT1";
+		public static readonly ReadOnlyByteVector TIT2 = "TIT2";
+		public static readonly ReadOnlyByteVector TIME = "TIME";
+		public static readonly ReadOnlyByteVector TOLY = "TOLY";
+		public static readonly ReadOnlyByteVector TOPE = "TOPE";
+		public static readonly ReadOnlyByteVector TPE1 = "TPE1";
+		public static readonly ReadOnlyByteVector TPE2 = "TPE2";
+		public static readonly ReadOnlyByteVector TPE3 = "TPE3";
+		public static readonly ReadOnlyByteVector TPE4 = "TPE4";
+		public static readonly ReadOnlyByteVector TPOS = "TPOS";
+		public static readonly ReadOnlyByteVector TRCK = "TRCK";
+		public static readonly ReadOnlyByteVector TRDA = "TRDA";
+		public static readonly ReadOnlyByteVector TSIZ = "TSIZ";
+		public static readonly ReadOnlyByteVector TSOA = "TSOA"; // Album Title Sort Frame
+		public static readonly ReadOnlyByteVector TSO2 = "TSO2"; // Album Artist Sort Frame
+		public static readonly ReadOnlyByteVector TSOC = "TSOC"; // Composer Sort Frame
+		public static readonly ReadOnlyByteVector TSOP = "TSOP"; // Performer Sort Frame
+		public static readonly ReadOnlyByteVector TSOT = "TSOT"; // Track Title Sort Frame
+		public static readonly ReadOnlyByteVector TXXX = "TXXX";
+		public static readonly ReadOnlyByteVector TYER = "TYER";
+		public static readonly ReadOnlyByteVector UFID = "UFID";
+		public static readonly ReadOnlyByteVector USER = "USER";
+		public static readonly ReadOnlyByteVector USLT = "USLT";
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/AttachedPictureFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/AttachedPictureFrame.cs
new file mode 100644
index 0000000..4762698
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/AttachedPictureFrame.cs
@@ -0,0 +1,705 @@
+//
+// AttachedPictureFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   attachedpictureframe.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2004 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+
+using TagLib;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Attached Picture (APIC) Frames.
+	/// </summary>
+	/// <remarks>
+	///    <para>A <see cref="AttachedPictureFrame" /> is used for storing
+	///    pictures that complement the media, including the album cover,
+	///    the physical medium, leaflets, file icons, etc. Other file and
+	///    object data can be encapulsated via <see
+	///    cref="GeneralEncapsulatedObjectFrame" />.</para>
+	///    <para>Additionally, <see cref="TagLib.Tag.Pictures" /> provides a
+	///    generic way or getting and setting pictures which is preferable
+	///    to format specific code.</para>
+	/// </remarks>
+	public class AttachedPictureFrame : Frame, IPicture
+	{
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the text encoding to use when rendering.
+		/// </summary>
+		private StringType text_encoding = Tag.DefaultEncoding;
+		
+		/// <summary>
+		///    Contains the mime type of <see cref="data" />.
+		/// </summary>
+		private string mime_type = null;
+		
+		/// <summary>
+		///    Contains the type of picture.
+		/// </summary>
+		private PictureType type = PictureType.Other;
+		
+		/// <summary>
+		///    Contains the description.
+		/// </summary>
+		private string description = null;
+		
+		/// <summary>
+		///    Contains the picture data.
+		/// </summary>
+		private ByteVector data = null;
+		
+		/// <summary>
+		///    Contains the raw field data of the current instance as
+		///    sent to <see cref="ParseFields" /> or <see
+		///    langword="null" /> if <see cref="ParseFields" /> has not
+		///    been called or <see cref="ParseRawData" /> has been
+		///    called.
+		/// </summary>
+		/// <remarks>
+		///    As this frame takes a while to parse and isn't read in
+		///    all cases, the raw data is stored here until it is
+		///    needed. This speeds up the file read time significantly.
+		/// </remarks>
+		private ByteVector raw_data = null;
+		
+		/// <summary>
+		///    Contains the ID3v2 version <see cref="raw_data" /> is
+		///    stored in.
+		/// </summary>
+		private byte raw_version = 0;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AttachedPictureFrame" /> with no contents and the
+		///    default values.
+		/// </summary>
+		/// <remarks>
+		///    <para>When a frame is created, it is not automatically
+		///    added to the tag. Consider using <see
+		///    cref="Get(Tag,string,PictureType,bool)" /> for more
+		///    integrated frame creation.</para>
+		///    <para>Additionally, <see cref="TagLib.Tag.Pictures" />
+		///    provides a generic way or getting and setting
+		///    pictures which is preferable to format specific
+		///    code.</para>
+		/// </remarks>
+		public AttachedPictureFrame () : base (FrameType.APIC, 4)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AttachedPictureFrame" /> by populating it with
+		///    the contents of another <see cref="IPicture" /> object.
+		/// </summary>
+		/// <param name="picture">
+		///    A <see cref="IPicture" /> object containing values to use
+		///    in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="picture" /> is <see langword="null" />.
+		/// </exception>
+		/// <remarks>
+		///    <para>When a frame is created, it is not automatically
+		///    added to the tag. Consider using <see
+		///    cref="Get(Tag,string,PictureType,bool)" /> for more
+		///    integrated frame creation.</para>
+		///    <para>Additionally, <see cref="TagLib.Tag.Pictures" />
+		///    provides a generic way or getting and setting
+		///    pictures which is preferable to format specific
+		///    code.</para>
+		/// </remarks>
+		/// <example>
+		///    <para>Add a picture to a file.</para>
+		///    <code lang="C#">
+		/// using TagLib;
+		/// using TagLib.Id3v2;
+		///
+		/// public static class AddId3v2Picture
+		/// {
+		/// 	public static void Main (string [] args)
+		/// 	{
+		/// 		if (args.Length != 2)
+		/// 			throw new ApplicationException (
+		/// 				"USAGE: AddId3v2Picture.exe AUDIO_FILE PICTURE_FILE");
+		///
+		/// 		// Create the file. Can throw file to TagLib# exceptions.
+		/// 		File file = File.Create (args [0]);
+		///
+		/// 		// Get or create the ID3v2 tag.
+		/// 		TagLib.Id3v2.Tag tag = file.GetTag (TagTypes.Id3v2, true) as TagLib.Id3v2.Tag;
+		/// 		if (tag == null)
+		/// 			throw new ApplicationException ("File does not support ID3v2 tags.");
+		///
+		/// 		// Create a picture. Can throw file related exceptions.
+		///		TagLib.Picture picture = TagLib.Picture.CreateFromPath (path);
+		///
+		/// 		// Add a new picture frame to the tag.
+		/// 		tag.AddFrame (new AttachedPictureFrame (picture));
+		///
+		/// 		// Save the file.
+		/// 		file.Save ();
+		/// 	}
+		/// }
+		///    </code>
+		/// </example>
+		public AttachedPictureFrame (IPicture picture)
+			: base(FrameType.APIC, 4)
+		{
+			if (picture == null)
+				throw new ArgumentNullException ("picture");
+			
+			mime_type   = picture.MimeType;
+			type        = picture.Type;
+			description = picture.Description;
+			data        = picture.Data;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AttachedPictureFrame" /> by reading its raw data in
+		///    a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public AttachedPictureFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AttachedPictureFrame" /> by reading its raw data
+		///    in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal AttachedPictureFrame (ByteVector data,
+		                                         int offset,
+		                                         FrameHeader header,
+		                                         byte version)
+			: base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the text encoding to use when storing the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the text encoding to
+		///    use when storing the current instance.
+		/// </value>
+		/// <remarks>
+		///    This encoding is overridden when rendering if <see
+		///    cref="Tag.ForceDefaultEncoding" /> is <see
+		///    langword="true" /> or the render version does not support
+		///    it.
+		/// </remarks>
+		public StringType TextEncoding {
+			get {ParseRawData (); return text_encoding;}
+			set {text_encoding = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the mime-type of the picture stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the mime-type of the
+		///    picture stored in the current instance.
+		/// </value>
+		public string MimeType {
+			get {
+				ParseRawData ();
+				if (mime_type != null)
+					return mime_type;
+				
+				return string.Empty;
+			}
+			set {mime_type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the picture type stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the picture type
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description and type per tag.
+		/// </remarks>
+		public PictureType Type {
+			get {ParseRawData (); return type;}
+			set {type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the description stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the description
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description and type per tag.
+		/// </remarks>
+		public string Description {
+			get {
+				ParseRawData ();
+				if (description != null)
+					return description;
+				
+				return string.Empty;
+			}
+			set {description = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the image data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> containing the image data
+		///    stored in the current instance.
+		/// </value>
+		public ByteVector Data {
+			get {
+				ParseRawData ();
+				return data != null ? data : new ByteVector ();
+			}
+			set {data = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> representing the current
+		///    instance.
+		/// </returns>
+		public override string ToString ()
+		{
+			System.Text.StringBuilder builder
+				= new System.Text.StringBuilder ();
+			
+			if (string.IsNullOrEmpty (Description)) {
+				builder.Append (Description);
+				builder.Append (" ");
+			}
+			
+			builder.AppendFormat (
+				System.Globalization.CultureInfo.InvariantCulture,
+				"[{0}] {1} bytes", MimeType, Data.Count);
+			
+			return builder.ToString ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified picture frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="AttachedPictureFrame" /> object containing
+		///    the matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static AttachedPictureFrame Get (Tag tag,
+		                                        string description,
+		                                        bool create)
+		{
+			return Get (tag, description, PictureType.Other, create);
+		}
+		
+		/// <summary>
+		///    Gets a specified picture frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="PictureType" /> specifying the picture type
+		///    to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="AttachedPictureFrame" /> object containing
+		///    the matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static AttachedPictureFrame Get (Tag tag,
+		                                        PictureType type,
+		                                        bool create)
+		{
+			return Get (tag, null, type, create);
+		}
+		
+		/// <summary>
+		///    Gets a specified picture frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="PictureType" /> specifying the picture type
+		///    to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="AttachedPictureFrame" /> object containing
+		///    the matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		/// <example>
+		///    <para>Sets a cover image with a description. Because <see
+		///    cref="Get(Tag,string,PictureType,bool)" /> is used, if
+		///    the program is called again with the same audio file and
+		///    desciption, the picture will be overwritten with the new
+		///    one.</para>
+		///    <code lang="C#">
+		/// using TagLib;
+		/// using TagLib.Id3v2;
+		///
+		/// public static class SetId3v2Cover
+		/// {
+		/// 	public static void Main (string [] args)
+		/// 	{
+		/// 		if (args.Length != 3)
+		/// 			throw new ApplicationException (
+		/// 				"USAGE: SetId3v2Cover.exe AUDIO_FILE PICTURE_FILE DESCRIPTION");
+		///
+		/// 		// Create the file. Can throw file to TagLib# exceptions.
+		/// 		File file = File.Create (args [0]);
+		///
+		/// 		// Get or create the ID3v2 tag.
+		/// 		TagLib.Id3v2.Tag tag = file.GetTag (TagTypes.Id3v2, true) as TagLib.Id3v2.Tag;
+		/// 		if (tag == null)
+		/// 			throw new ApplicationException ("File does not support ID3v2 tags.");
+		///
+		/// 		// Create a picture. Can throw file related exceptions.
+		///		TagLib.Picture picture = TagLib.Picture.CreateFromPath (args [1]);
+		///
+		/// 		// Get or create the picture frame.
+		/// 		AttachedPictureFrame frame = AttachedPictureFrame.Get (
+		/// 			tag, args [2], PictureType.FrontCover, true);
+		///
+		/// 		// Set the data from the picture.
+		/// 		frame.MimeType = picture.MimeType;
+		/// 		frame.Data     = picture.data;
+		/// 		
+		/// 		// Save the file.
+		/// 		file.Save ();
+		/// 	}
+		/// }
+		///    </code>
+		/// </example>
+		public static AttachedPictureFrame Get (Tag tag,
+		                                        string description,
+		                                        PictureType type,
+		                                        bool create)
+		{
+			AttachedPictureFrame apic;
+			foreach (Frame frame in tag.GetFrames (FrameType.APIC)) {
+				apic = frame as AttachedPictureFrame;
+				
+				if (apic == null)
+					continue;
+				
+				if (description != null && apic.Description != description)
+					continue;
+				
+				if (type != PictureType.Other && apic.Type != type)
+					continue;
+				
+				return apic;
+			}
+			
+			if (!create)
+				return null;
+			
+			apic = new AttachedPictureFrame ();
+			apic.Description = description;
+			apic.Type = type;
+			
+			tag.AddFrame (apic);
+			
+			return apic;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 5 bytes.
+		/// </exception>
+		protected override void ParseFields (ByteVector data, byte version)
+		{
+			if (data.Count < 5)
+				throw new CorruptFileException (
+					"A picture frame must contain at least 5 bytes.");
+			
+			raw_data = data;
+			raw_version = version;
+		}
+		
+		/// <summary>
+		///    Performs the actual parsing of the raw data.
+		/// </summary>
+		/// <remarks>
+		///    Because of the high parsing cost and relatively low usage
+		///    of the class, <see cref="ParseFields" /> only stores the
+		///    field data so it can be parsed on demand. Whenever a
+		///    property or method is called which requires the data,
+		///    this method is called, and only on the first call does it
+		///    actually parse the data.
+		/// </remarks>
+		protected void ParseRawData ()
+		{
+			if (raw_data == null)
+				return;
+			
+			int pos = 0;
+			int offset;
+			
+			text_encoding = (StringType) raw_data [pos++];
+			
+			if (raw_version > 2) {
+				offset = raw_data.Find (ByteVector.TextDelimiter (
+					StringType.Latin1), pos);
+				
+				if(offset < pos)
+					return;
+				
+				mime_type = raw_data.ToString (
+					StringType.Latin1, pos, offset - pos);
+				pos = offset + 1;
+			} else {
+				ByteVector ext = raw_data.Mid (pos, 3);
+				
+				if (ext == "JPG")
+					mime_type = "image/jpeg";
+				else if (ext == "PNG")
+					mime_type = "image/png";
+				else
+					mime_type = "image/unknown";
+				
+				pos += 3;
+			}
+			
+			ByteVector delim = ByteVector.TextDelimiter (
+				text_encoding);
+			
+			type = (PictureType) raw_data [pos++];
+			
+			offset = raw_data.Find (delim, pos, delim.Count);
+			
+			if(offset < pos)
+				return;
+			
+			description = raw_data.ToString (text_encoding, pos,
+				offset - pos);
+			pos = offset + delim.Count;
+			raw_data.RemoveRange (0, pos);
+			this.data = raw_data;
+			
+			this.raw_data = null;
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			if (raw_data != null && raw_version == version)
+				return raw_data;
+			
+			StringType encoding = CorrectEncoding (TextEncoding,
+				version);
+			ByteVector data = new ByteVector ();
+			
+			data.Add ((byte) encoding);
+			
+			if (version == 2) {
+				switch (MimeType) {
+				case "image/png":
+					data.Add ("PNG");
+					break;
+				case "image/jpeg":
+					data.Add ("JPG");
+					break;
+				default:
+					data.Add ("XXX");
+					break;
+				}
+			} else {
+				data.Add (ByteVector.FromString (MimeType,
+					StringType.Latin1));
+				data.Add (ByteVector.TextDelimiter (
+					StringType.Latin1));
+			}
+			
+			data.Add ((byte) type);
+			data.Add (ByteVector.FromString (Description, encoding));
+			data.Add (ByteVector.TextDelimiter (encoding));
+			data.Add (this.data);
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			AttachedPictureFrame frame = new AttachedPictureFrame ();
+			frame.text_encoding = text_encoding;
+			frame.mime_type = mime_type;
+			frame.type = type;
+			frame.description = description;
+			if (data != null)
+				frame.data = new ByteVector (data);
+			if (raw_data != null)
+				frame.data = new ByteVector (raw_data);
+			frame.raw_version = raw_version;
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/CommentsFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/CommentsFrame.cs
new file mode 100644
index 0000000..deaecdd
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/CommentsFrame.cs
@@ -0,0 +1,539 @@
+//
+// CommentsFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v2commentsframe.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Comments (COMM) Frames.
+	/// </summary>
+	/// <remarks>
+	///    <para>A <see cref="CommentsFrame" /> should be used for storing
+	///    user readable comments on the media file.</para>
+	///    <para>When reading comments from a file, <see cref="GetPreferred"
+	///    /> should be used as it gracefully falls back to comments that
+	///    you, as a developer, may not be expecting. When writing comments,
+	///    however, it is best to use <see cref="Get" /> as it forces it to
+	///    be written in the exact version you are expecting.</para>
+	/// </remarks>
+	public class CommentsFrame : Frame
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the text encoding to use when rendering the
+		///    current instance.
+		/// </summary>
+		private StringType encoding = Tag.DefaultEncoding;
+		
+		/// <summary>
+		///    Contains the ISO-639-2 language code of the current
+		///    instance.
+		/// </summary>
+		private string language = null;
+		
+		/// <summary>
+		///    Contains the description of the current instance.
+		/// </summary>
+		private string description = null;
+		
+		/// <summary>
+		///    Contains the comment text of the current instance.
+		/// </summary>
+		private string text = null;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CommentsFrame" /> with a specified description,
+		///    ISO-639-2 language code, and text encoding.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code of the new frame.
+		/// </param>
+		/// <param name="encoding">
+		///    A <see cref="StringType" /> containing the text encoding
+		///    to use when rendering the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public CommentsFrame (string description, string language,
+		                      StringType encoding)
+			: base (FrameType.COMM, 4)
+		{
+			this.encoding    = encoding;
+			this.language    = language;
+			this.description = description;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CommentsFrame" /> with a specified description and
+		///    ISO-639-2 language code.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code of the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public CommentsFrame (string description, string language)
+			: this (description, language,
+			        TagLib.Id3v2.Tag.DefaultEncoding)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CommentsFrame" /> with a specified description.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public CommentsFrame (string description)
+			: this (description, null)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CommentsFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public CommentsFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CommentsFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal CommentsFrame (ByteVector data, int offset,
+		                                  FrameHeader header,
+		                                  byte version) : base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the text encoding to use when storing the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the text encoding to
+		///    use when storing the current instance.
+		/// </value>
+		/// <remarks>
+		///    This encoding is overridden when rendering if <see
+		///    cref="Tag.ForceDefaultEncoding" /> is <see
+		///    langword="true" /> or the render version does not support
+		///    it.
+		/// </remarks>
+		public StringType TextEncoding {
+			get {return encoding;}
+			set {encoding = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the ISO-639-2 language code stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one file with a matching description
+		///    and ISO-639-2 language code per tag.
+		/// </remarks>
+		public string Language {
+			get {
+				if (language != null && language.Length > 2)
+					return language.Substring (0, 3);
+				
+				return "XXX";
+			}
+			set {language = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the description stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the description
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description and ISO-639-2 language code per tag.
+		/// </remarks>
+		public string Description {
+			get {
+				if (description != null)
+					return description;
+				
+				return string.Empty;
+			}
+			set {description = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the comment text stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the comment text
+		///    stored in the current instance.
+		/// </value>
+		public string Text {
+			get {
+				if (text != null)
+					return text;
+				
+				return string.Empty;
+			}
+			set {text = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> containing the comment text.
+		/// </returns>
+		public override string ToString ()
+		{
+			return Text;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified comments frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> specifying the ISO-639-2 language
+		///   code to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="CommentsFrame" /> object containing the
+		///    matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static CommentsFrame Get (Tag tag, string description,
+		                                 string language, bool create)
+		{
+			CommentsFrame comm;
+			foreach (Frame frame in tag.GetFrames (FrameType.COMM)) {
+				comm = frame as CommentsFrame;
+				
+				if (comm == null)
+					continue;
+				
+				if (comm.Description != description)
+					continue;
+				
+				if (language != null && language != comm.Language)
+					continue;
+				
+				return comm;
+			}
+			
+			if (!create)
+				return null;
+			
+			comm = new CommentsFrame (description, language);
+			tag.AddFrame (comm);
+			return comm;
+		}
+		
+		/// <summary>
+		///    Gets a specified comments frame from the specified tag,
+		///    trying to to match the description and language but
+		///    accepting an incomplete match.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> specifying the ISO-639-2 language
+		///   code to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="CommentsFrame" /> object containing the
+		///    matching frame, or <see langword="null" /> if a match
+		///    wasn't found.
+		/// </returns>
+		/// <remarks>
+		///    <para>The method tries matching with the following order
+		///    of precidence:</para>
+		///    <list type="number">
+		///       <item><term>The first frame with a matching
+		///       description and language.</term></item>
+		///       <item><term>The first frame with a matching
+		///       language.</term></item>
+		///       <item><term>The first frame with a matching
+		///       description.</term></item>
+		///       <item><term>The first frame.</term></item>
+		///    </list>
+		/// </remarks>
+		public static CommentsFrame GetPreferred (Tag tag,
+		                                          string description,
+		                                          string language)
+		{
+			// This is weird, so bear with me. The best thing we can
+			// have is something straightforward and in our own
+			// language. If it has a description, then it is
+			// probably used for something other than an actual
+			// comment. If that doesn't work, we'd still rather have
+			// something in our language than something in another.
+			// After that all we have left are things in other
+			// languages, so we'd rather have one with actual
+			// content, so we try to get one with no description
+			// first.
+
+			bool skip_itunes = description == null ||
+				!description.StartsWith ("iTun");
+			
+			int best_value = -1;
+			CommentsFrame best_frame = null;
+			
+			foreach (Frame frame in tag.GetFrames (FrameType.COMM)) {
+				CommentsFrame comm = frame as CommentsFrame;
+				
+				if (comm == null)
+					continue;
+				
+				if (skip_itunes &&
+					comm.Description.StartsWith ("iTun"))
+					continue;
+				
+				bool same_name = comm.Description == description;
+				bool same_lang = comm.Language == language;
+				
+				if (same_name && same_lang)
+					return comm;
+				
+				int value = same_lang ? 2 : same_name ? 1 : 0;
+				
+				if (value <= best_value)
+					continue;
+				
+				best_value = value;
+				best_frame = comm;
+			}
+			
+			return best_frame;
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			if (data.Count < 4)
+				throw new CorruptFileException (
+					"Not enough bytes in field.");
+			
+			encoding = (StringType) data [0];
+			language = data.ToString (StringType.Latin1, 1, 3);
+			
+			// Instead of splitting into two string, in the format
+			// [{desc}\0{value}], try splitting into three strings
+			// in case of a misformatted [{desc}\0{value}\0].
+			string [] split = data.ToStrings (encoding, 4, 3);
+			
+			if (split.Length == 0) {
+				// No data in the frame.
+				description = String.Empty;
+				text        = String.Empty;
+			} else if (split.Length == 1) {
+				// Bad comment frame. Assume that it lacks a
+				// description.
+				description = String.Empty;
+				text        = split [0];
+			} else {
+				description = split [0];
+				text        = split [1];
+			}
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			StringType encoding = CorrectEncoding (TextEncoding,
+				version);
+			ByteVector v = new ByteVector ();
+			
+			v.Add ((byte) encoding);
+			v.Add (ByteVector.FromString (Language, StringType.Latin1));
+			v.Add (ByteVector.FromString (description, encoding));
+			v.Add (ByteVector.TextDelimiter (encoding));
+			v.Add (ByteVector.FromString (text, encoding));
+			
+			return v;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			CommentsFrame frame = new CommentsFrame (description,
+				language, encoding);
+			frame.text = text;
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/GeneralEncapsulatedObjectFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/GeneralEncapsulatedObjectFrame.cs
new file mode 100644
index 0000000..7d778cf
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/GeneralEncapsulatedObjectFrame.cs
@@ -0,0 +1,450 @@
+//
+// GeneralEncapsulatedObjectFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   generalencapsulatedobjectframe.cpp from TagLib
+//
+// Copyright (C) 2007 Brian Nickel
+// Copyright (C) 2007 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 General Encapsulated Object (GEOB) Frames.
+	/// </summary>
+	/// <remarks>
+	///    <para>A <see cref="GeneralEncapsulatedObjectFrame" /> should be
+	///    used for storing files and other objects relevant to the file but
+	///    not supported by other frames.</para>
+	/// </remarks>
+	public class GeneralEncapsulatedObjectFrame : Frame
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the text encoding to use when rendering the
+		///    current instance.
+		/// </summary>
+		private StringType encoding = Tag.DefaultEncoding;
+		
+		/// <summary>
+		///    Contains the mime type of <see cref="data" />.
+		/// </summary>
+		private string mime_type = null;
+		
+		/// <summary>
+		///    Contains the original file name.
+		/// </summary>
+		string file_name = null;
+		
+		/// <summary>
+		///    Contains the description.
+		/// </summary>
+		private string description = null;
+		
+		/// <summary>
+		///    Contains the data.
+		/// </summary>
+		private ByteVector data = null;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="GeneralEncapsulatedObjectFrame" /> with no
+		///    contents.
+		/// </summary>
+		public GeneralEncapsulatedObjectFrame ()
+			: base (FrameType.GEOB, 4)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="GeneralEncapsulatedObjectFrame" /> by reading its
+		///    raw data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public GeneralEncapsulatedObjectFrame (ByteVector data,
+		                                       byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="GeneralEncapsulatedObjectFrame" /> by reading its
+		///    raw data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal GeneralEncapsulatedObjectFrame (ByteVector data,
+		                                                   int offset,
+		                                                   FrameHeader header,
+		                                                   byte version)
+			: base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the text encoding to use when storing the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the text encoding to
+		///    use when storing the current instance.
+		/// </value>
+		/// <remarks>
+		///    This encoding is overridden when rendering if <see
+		///    cref="Tag.ForceDefaultEncoding" /> is <see
+		///    langword="true" /> or the render version does not support
+		///    it.
+		/// </remarks>
+		public StringType TextEncoding {
+			get {return encoding;}
+			set {encoding = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the mime-type of the object stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the mime-type of the
+		///    object stored in the current instance.
+		/// </value>
+		public string MimeType {
+			get {
+				if (mime_type != null)
+					return mime_type;
+				
+				return string.Empty;
+			}
+			set {mime_type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the file name of the object stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the file name of the
+		///    object stored in the current instance.
+		/// </value>
+		public string FileName {
+			get {
+				if (file_name != null)
+					return file_name;
+				
+				return string.Empty;
+			}
+			set {file_name = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the description stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the description
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description and type per tag.
+		/// </remarks>
+		public string Description {
+			get {
+				if (description != null)
+					return description;
+				
+				return string.Empty;
+			}
+			set {description = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the object data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> containing the object data
+		///    stored in the current instance.
+		/// </value>
+		public ByteVector Object {
+			get {return data != null ? data : new ByteVector ();}
+			set {data = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Creates a text description of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> object containing a description
+		///    of the current instance.
+		/// </returns>
+		public override string ToString ()
+		{
+			System.Text.StringBuilder builder
+				= new System.Text.StringBuilder ();
+			
+			if (Description.Length == 0) {
+				builder.Append (Description);
+				builder.Append (" ");
+			}
+			
+			builder.AppendFormat (
+				System.Globalization.CultureInfo.InvariantCulture,
+				"[{0}] {1} bytes", MimeType, Object.Count);
+			
+			return builder.ToString ();
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified encapsulated object frame from the
+		///    specified tag, optionally creating it if it does not
+		///    exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="GeneralEncapsulatedObjectFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found and <paramref name="create" /> is
+		///    <see langword="false" />.
+		/// </returns>
+		public static GeneralEncapsulatedObjectFrame Get (Tag tag,
+		                                                  string description,
+		                                                  bool create)
+		{
+			GeneralEncapsulatedObjectFrame geob;
+			foreach (Frame frame in tag.GetFrames (FrameType.GEOB)) {
+				geob = frame as GeneralEncapsulatedObjectFrame;
+				
+				if (geob == null)
+					continue;
+				
+				if (geob.Description != description)
+					continue;
+				
+				return geob;
+			}
+			
+			if (!create)
+				return null;
+			
+			geob = new GeneralEncapsulatedObjectFrame ();
+			geob.Description = description;
+			tag.AddFrame (geob);
+			return geob;
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 5 bytes.
+		/// </exception>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			if (data.Count < 4)
+				throw new CorruptFileException (
+					"An object frame must contain at least 4 bytes.");
+			
+			int start = 0;
+			
+			encoding =  (StringType) data [start++];
+			
+			int end = data.Find (
+				ByteVector.TextDelimiter (StringType.Latin1),
+				start);
+			
+			if (end < start)
+				return;
+			
+			mime_type = data.ToString (StringType.Latin1, start,
+				end - start);
+			
+			ByteVector delim = ByteVector.TextDelimiter (
+				encoding);
+			start = end + 1;
+			end = data.Find (delim, start, delim.Count);
+			
+			if (end < start)
+				return;
+			
+			file_name = data.ToString (encoding, start,
+				end - start);
+			start = end + delim.Count;
+			end = data.Find (delim, start, delim.Count);
+			
+			if (end < start)
+				return;
+			
+			description = data.ToString (encoding, start,
+				end - start);
+			start = end + delim.Count;
+			
+			data.RemoveRange (0, start);
+			this.data = data;
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			StringType encoding = CorrectEncoding (this.encoding,
+				version);
+			ByteVector v = new ByteVector ();
+			
+			v.Add ((byte) encoding);
+			
+			if (MimeType != null)
+				v.Add (ByteVector.FromString (MimeType,
+					StringType.Latin1));
+			v.Add (ByteVector.TextDelimiter (StringType.Latin1));
+			
+			if (FileName != null)
+				v.Add (ByteVector.FromString (FileName,
+					encoding));
+			v.Add (ByteVector.TextDelimiter (encoding));
+			
+			if (Description != null)
+				v.Add (ByteVector.FromString (Description,
+					encoding));
+			v.Add (ByteVector.TextDelimiter (encoding));
+			
+			v.Add (data);
+			return v;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			GeneralEncapsulatedObjectFrame frame =
+				new GeneralEncapsulatedObjectFrame ();
+			frame.encoding = encoding;
+			frame.mime_type = mime_type;
+			frame.file_name = file_name;
+			frame.description = description;
+			if (data != null)
+				frame.data = new ByteVector (data);
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/MusicCdIdentifierFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/MusicCdIdentifierFrame.cs
new file mode 100644
index 0000000..9abf9d1
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/MusicCdIdentifierFrame.cs
@@ -0,0 +1,328 @@
+//
+// MusicCdIdentifierFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Music CD Identifier (MCDI) Frames.
+	/// </summary>
+	/// <remarks>
+	///    Music CD Identifier Frames should contain the table of
+	///    contents data as stored on the physical CD. It is primarily used
+	///    for track information lookup by through web sources like CDDB.
+	/// </remarks>
+	/// <example>
+	///    <para>Reading the music CD identifier from a tag.</para>
+	///    <code lang="C#">
+	/// using TagLib;
+	/// using TagLib.Id3v2;
+	/// 
+	/// public static class LookupUtil
+	/// {
+	/// 	public static ByteVector GetCdIdentifier (string filename)
+	/// 	{
+	/// 		File file = File.Create (filename, ReadStyle.None);
+	/// 		Id3v2.Tag tag = file.GetTag (TagTypes.Id3v2, false) as Id3v2.Tag;
+	/// 		if (tag == null)
+	/// 			return new ByteVector ();
+	/// 		
+	/// 		MusicCdIdentifierFrame frame = MusicCdIdentifierFrame.Get (tag, false);
+	/// 		if (frame == null)
+	/// 			return new ByteVector ();
+	///
+	/// 		return frame.Data;
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="C++">
+	/// #using &lt;System.dll>
+	/// #using &lt;taglib-sharp.dll>
+	///
+	/// using System;
+	/// using TagLib;
+	/// using TagLib::Id3v2;
+	/// 
+	/// public ref class LookupUtil abstract sealed
+	/// {
+	/// public:
+	/// 	static ByteVector^ GetCdIdentifier (String^ filename)
+	/// 	{
+	/// 		File^ file = File::Create (filename, ReadStyle::None);
+	/// 		Id3v2::Tag^ tag = dynamic_cast&lt;Id3v2::Tag^> (file.GetTag (TagTypes::Id3v2, false));
+	/// 		if (tag == null)
+	/// 			return gcnew ByteVector;
+	/// 		
+	/// 		MusicCdIdentifierFrame^ frame = MusicCdIdentifierFrame::Get (tag, false);
+	/// 		if (frame == null)
+	/// 			return gcnew ByteVector;
+	///
+	/// 		return frame->Data;
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="VB">
+	/// Imports TagLib
+	/// Imports TagLib.Id3v2
+	/// 
+	/// Public Shared Class LookupUtil
+	/// 	Public Shared Sub GetCdIdentifier (filename As String) As TagLib.ByteVector
+	/// 		Dim file As File = File.Create (filename, ReadStyle.None)
+	/// 		Dim tag As Id3v2.Tag = file.GetTag (TagTypes.Id3v2, False)
+	/// 		If tag Is Nothing Return New ByteVector ()
+	/// 		
+	/// 		Dim frame As MusicCdIdentifierFrame = MusicCdIdentifierFrame.Get (tag, False)
+	/// 		If frame Is Nothing Return New ByteVector ()
+	///
+	/// 		Return frame.Data
+	/// 	End Sub
+	/// End Class
+	///    </code>
+	///    <code lang="Boo">
+	/// import TagLib
+	/// import TagLib.Id3v2
+	/// 
+	/// public static class LookupUtil:
+	/// 	static def GetCdIdentifier (filename as string) as TagLib.ByteVector:
+	/// 		file as File = File.Create (filename, ReadStyle.None)
+	/// 		tag as Id3v2.Tag = file.GetTag (TagTypes.Id3v2, false)
+	/// 		if tag == null:
+	/// 			return ByteVector ()
+	/// 		
+	/// 		frame as MusicCdIdentifierFrame = MusicCdIdentifierFrame.Get (tag, false)
+	/// 		if frame == null:
+	/// 			return ByteVector ()
+	///
+	/// 		return frame.Data
+	///    </code>
+	/// </example>
+	public class MusicCdIdentifierFrame : Frame
+	{
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the identifer data for the current instance.
+		/// </summary>
+		private ByteVector field_data = null;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MusicCdIdentifierFrame" /> with empty
+		///    identifier data.
+		/// </summary>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public MusicCdIdentifierFrame () : base (FrameType.MCDI, 4)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MusicCdIdentifierFrame" /> by reading its raw data
+		///    in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public MusicCdIdentifierFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MusicCdIdentifierFrame" /> by reading its raw data
+		///    in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal MusicCdIdentifierFrame (ByteVector data,
+		                                           int offset,
+		                                           FrameHeader header,
+		                                           byte version)
+			: base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the identifier data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> containing the identifier
+		///    data stored in the current instance.
+		/// </value>
+		public ByteVector Data {
+			get {return field_data;}
+			set {field_data = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a music CD identifier frame from a specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="MusicCdIdentifierFrame" /> object containing
+		///    the matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static MusicCdIdentifierFrame Get (Tag tag, bool create)
+		{
+			MusicCdIdentifierFrame mcdi;
+			foreach (Frame frame in tag) {
+				mcdi = frame as MusicCdIdentifierFrame;
+				
+				if (mcdi != null)
+					return mcdi;
+			}
+			
+			if (!create)
+				return null;
+			
+			mcdi = new MusicCdIdentifierFrame ();
+			tag.AddFrame (mcdi);
+			return mcdi;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			field_data = data;
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			return field_data != null ? field_data : new ByteVector ();
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			MusicCdIdentifierFrame frame = new MusicCdIdentifierFrame ();
+			if (field_data != null)
+				frame.field_data = new ByteVector (field_data);
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/PlayCountFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/PlayCountFrame.cs
new file mode 100644
index 0000000..ef0bc51
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/PlayCountFrame.cs
@@ -0,0 +1,361 @@
+//
+// PlayCountFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Play Count (PCNT) Frames.
+	/// </summary>
+	/// <example>
+	///    <para>Getting and incrementing the play count of a file.</para>
+	///    <code lang="C#">
+	/// using TagLib;
+	/// using TagLib.Id3v2;
+	///
+	/// public static class TrackUtil
+	/// {
+	/// 	public static int GetPlayCount (string filename)
+	/// 	{
+	/// 		File file = File.Create (filename, ReadStyle.None);
+	/// 		Id3v2.Tag tag = file.GetTag (TagTypes.Id3v2, false) as Id3v2.Tag;
+	/// 		if (tag == null)
+	/// 			return 0;
+	/// 		
+	/// 		PlayCountFrame frame = PlayCountFrame.Get (tag, false);
+	/// 		if (frame == null)
+	/// 			return 0;
+	///
+	/// 		return frame.PlayCount;
+	/// 	}
+	/// 	
+	/// 	public static void IncrementPlayCount (string filename)
+	/// 	{
+	/// 		File file = File.Create (filename, ReadStyle.None);
+	/// 		Id3v2.Tag tag = file.GetTag (TagTypes.Id3v2, true) as Id3v2.Tag;
+	/// 		if (tag == null)
+	/// 			return;
+	/// 		
+	/// 		PlayCountFrame.Get (tag, true).PlayCount ++;
+	/// 		file.Save ();
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="C++">
+	/// #using &lt;System.dll>
+	/// #using &lt;taglib-sharp.dll>
+	///
+	/// using System;
+	/// using TagLib;
+	/// using TagLib::Id3v2;
+	///
+	/// public ref class TrackUtil abstract sealed
+	/// {
+	/// public:
+	/// 	static int GetPlayCount (String^ filename)
+	/// 	{
+	/// 		File^ file = File.Create (filename, ReadStyle.None);
+	/// 		Id3v2::Tag^ tag = dynamic_cast&lt;Id3v2::Tag^> (file.GetTag (TagTypes::Id3v2, false));
+	/// 		if (tag == null)
+	/// 			return 0;
+	/// 		
+	/// 		PlayCountFrame^ frame = PlayCountFrame::Get (tag, false);
+	/// 		if (frame == null)
+	/// 			return 0;
+	///
+	/// 		return frame->PlayCount;
+	/// 	}
+	/// 	
+	/// 	static void IncrementPlayCount (String^ filename)
+	/// 	{
+	/// 		File^ file = File::Create (filename, ReadStyle::None);
+	/// 		Id3v2.Tag^ tag = dynamic_cast&lt;Id3v2::Tag^> (file.GetTag (TagTypes::Id3v2, true));
+	/// 		if (tag == null)
+	/// 			return;
+	/// 		
+	/// 		PlayCountFrame::Get (tag, true)->PlayCount ++;
+	/// 		file->Save ();
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="VB">
+	/// Imports TagLib
+	/// Imports TagLib.Id3v2
+	///
+	/// Public Shared Class TrackUtil
+	/// 	Public Shared Sub GetPlayCount (filename As String) As Integer
+	/// 		Dim file As File = File.Create (filename, ReadStyle.None)
+	/// 		Dim tag As Id3v2.Tag = file.GetTag (TagTypes.Id3v2, False)
+	/// 		If tag Is Nothing Then Return 0
+	/// 		
+	/// 		Dim frame As PlayCountFrame = PlayCountFrame.Get (tag, False)
+	///		If frame Is Nothing Then Return 0
+	///
+	/// 		Return frame.PlayCount
+	/// 	End Sub
+	///
+	///	Public Shared Sub IncrementPlayCount (filename As String)
+	/// 		Dim file As File = File.Create (filename, ReadStyle.None)
+	/// 		Dim tag As Id3v2.Tag = file.GetTag (TagTypes.Id3v2, True)
+	/// 		If tag Is Nothing Then Exit Sub
+	/// 		
+	/// 		PlayCountFrame.Get (tag, True).PlayCount += 1
+	/// 		file.Save ()
+	/// 	End Sub
+	/// End Class
+	///    </code>
+	///    <code lang="Boo">
+	/// import TagLib
+	/// import TagLib.Id3v2
+	/// 
+	/// public static class TrackUtil:
+	/// 	static def GetPlayCount (filename as string) as int:
+	/// 		file As File = File.Create (filename, ReadStyle.None)
+	/// 		tag as Id3v2.Tag = file.GetTag (TagTypes.Id3v2, false)
+	///		if tag == null:
+	/// 			return 0
+	/// 		
+	/// 		frame as PlayCountFrame = PlayCountFrame.Get (tag, false)
+	/// 		if frame == null:
+	///			return 0
+	///
+	/// 		return frame.PlayCount
+	///
+	///	static def IncrementPlayCount (filename as string):
+	/// 		file as File = File.Create (filename, ReadStyle.None)
+	/// 		tag as Id3v2.Tag = file.GetTag (TagTypes.Id3v2, True)
+	///		if tag == null:
+	/// 			return
+	/// 		
+	/// 		PlayCountFrame.Get (tag, true).PlayCount ++
+	/// 		file.Save ()
+	///    </code>
+	/// </example>
+	public class PlayCountFrame : Frame
+	{
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the total number of times the file has been
+		///    played.
+		/// </summary>
+		private ulong play_count = 0;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PlayCountFrame" /> with a count of zero.
+		/// </summary>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public PlayCountFrame () : base (FrameType.PCNT, 4)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PlayCountFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public PlayCountFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PlayCountFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal PlayCountFrame (ByteVector data, int offset,
+		                                   FrameHeader header,
+		                                   byte version) : base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the play count of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong" /> containing the play count of the
+		///    current instance.
+		/// </value>
+		public ulong PlayCount {
+			get {return play_count;}
+			set {play_count = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a play count frame from a specified tag, optionally
+		///    creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="PlayCountFrame" /> object containing the
+		///    matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static PlayCountFrame Get (Tag tag, bool create)
+		{
+			PlayCountFrame pcnt;
+			foreach (Frame frame in tag) {
+				pcnt = frame as PlayCountFrame;
+				
+				if (pcnt != null)
+					return pcnt;
+			}
+			
+			if (!create)
+				return null;
+			
+			pcnt = new PlayCountFrame ();
+			tag.AddFrame (pcnt);
+			return pcnt;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data, byte version)
+		{
+			play_count = data.ToULong ();
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			ByteVector data = ByteVector.FromULong (play_count);
+			while (data.Count > 4 && data [0] == 0)
+				data.RemoveAt (0);
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			PlayCountFrame frame = new PlayCountFrame ();
+			frame.play_count = play_count;
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/PopularimeterFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/PopularimeterFrame.cs
new file mode 100644
index 0000000..3a154a2
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/PopularimeterFrame.cs
@@ -0,0 +1,297 @@
+//
+// PopularimeterFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Popularimeter (POPM) Frames.
+	/// </summary>
+	public class PopularimeterFrame : Frame
+	{
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the email of the user this frame belongs to.
+		/// </summary>
+		private string user = string.Empty;
+		
+		/// <summary>
+		///    Contains the rating of the files from 0 to 255.
+		/// </summary>
+		private byte rating = 0;
+		
+		/// <summary>
+		///    Contains the number of times this file has been played.
+		/// </summary>
+		private ulong play_count = 0;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PopularimeterFrame" /> for a specified user with a
+		///    rating and play count of zero.
+		/// </summary>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public PopularimeterFrame (string user)
+			: base (FrameType.POPM, 4)
+		{
+			User = user;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PopularimeterFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public PopularimeterFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PopularimeterFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal PopularimeterFrame (ByteVector data,
+		                                       int offset,
+		                                       FrameHeader header,
+		                                       byte version)
+			: base (header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the user to whom the current instance
+		///    belongs.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the user to whom the
+		///    current instance belongs.
+		/// </value>
+		public string User {
+			get {return user;}
+			set {user = value != null ? value : string.Empty;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the rating of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> containing the rating of the
+		///    current instance.
+		/// </value>
+		public byte Rating {
+			get {return rating;}
+			set {rating = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the play count of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong" /> containing the play count of the
+		///    current instance.
+		/// </value>
+		public ulong PlayCount {
+			get {return play_count;}
+			set {play_count = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a popularimeter frame from a specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="user">
+		///    A <see cref="string" /> containing the user to search for
+		///    in the current instance.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="PopularimeterFrame" /> object containing the
+		///    matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static PopularimeterFrame Get (Tag tag, string user,
+		                                      bool create)
+		{
+			PopularimeterFrame popm;
+			foreach (Frame frame in tag) {
+				popm = frame as PopularimeterFrame;
+				
+				if (popm != null && popm.user.Equals (user))
+					return popm;
+			}
+			
+			if (!create)
+				return null;
+			
+			popm = new PopularimeterFrame (user);
+			tag.AddFrame (popm);
+			return popm;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			ByteVector delim = ByteVector.TextDelimiter (
+				StringType.Latin1);
+			
+			int index = data.Find (delim);
+			if (index < 0)
+				throw new CorruptFileException (
+					"Popularimeter frame does not contain a text delimiter");
+			
+			user = data.ToString (StringType.Latin1, 0, index);
+			rating = data [index + 1];
+			play_count = data.Mid (index + 2).ToULong ();
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			ByteVector data = ByteVector.FromULong (play_count);
+			while (data.Count > 0 && data [0] == 0)
+				data.RemoveAt (0);
+			
+			data.Insert (0, rating);
+			data.Insert (0, 0);
+			data.Insert (0, ByteVector.FromString (user,
+				StringType.Latin1));
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			PopularimeterFrame frame = new PopularimeterFrame (user);
+			frame.play_count = play_count;
+			frame.rating = rating;
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/PrivateFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/PrivateFrame.cs
new file mode 100644
index 0000000..417b77f
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/PrivateFrame.cs
@@ -0,0 +1,432 @@
+//
+// PrivateFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// 
+// 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v2 {	
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Private (PRIV) Frames.
+	/// </summary>
+	/// <remarks>
+	///    <para>A <see cref="PrivateFrame" /> should be used for storing
+	///    values specific to the application that cannot or should not be
+	///    stored in another frame type.</para>
+	/// </remarks>
+	/// <example>
+	///    <para>Serializing a database entry and storing it in a private
+	///    field.</para>
+	///    <code lang="C#">
+	/// using System;
+	/// using System.IO;
+	/// using System.Runtime.Serialization;
+	/// using System.Text;
+	/// using System.Xml.Serialization;
+	/// using TagLib.Id3v2;
+	///
+	/// public static class DbUtil
+	/// {
+	/// 	public static void StoreDatabaseEntry (Tag tag, ISerializable dbEntry)
+	/// 	{
+	/// 		StringWriter data = new StringWriter (new StringBuilder ());
+	/// 		XmlSerializer serializer = new XmlSerializer (dbEntry.GetType ());
+	/// 		serializer.Serialize (data, dbEntry);
+	/// 		PrivateFrame frame = PrivateFrame.Get (tag, "org.MyProgram.DatabaseEntry", true);
+	/// 		frame.PrivateData = Encoding.UTF8.GetBytes (data.ToString ());
+	/// 	}
+	/// 	
+	/// 	public static object GetDatabaseEntry (Tag tag, Type type)
+	/// 	{
+	/// 		PrivateFrame frame = PrivateFrame.Get (tag, "org.MyProgram.DatabaseEntry", false);
+	/// 		if (frame == null)
+	/// 			return null;
+	/// 	
+	/// 		XmlSerializer serializer = new XmlSerializer (type);
+	/// 		return serializer.Deserialize (new MemoryStream (frame.PrivateData));
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="C++">
+	/// #using &lt;System.dll>
+	/// #using &lt;System.Xml.dll>
+	/// #using &lt;taglib-sharp.dll>
+	/// 
+	/// using System;
+	/// using System::IO;
+	/// using System::Runtime::Serialization;
+	/// using System::Text;
+	/// using System::Xml::Serialization;
+	/// using TagLib::Id3v2;
+	/// 
+	/// public ref class DbUtil abstract sealed
+	/// {
+	/// public:
+	/// 	static void StoreDatabaseEntry (Tag^ tag, ISerializable^ dbEntry)
+	/// 	{
+	/// 		StringWriter^ data = gcnew StringWriter (gcnew StringBuilder);
+	/// 		XmlSerializer serializer = gcnew XmlSerializer (dbEntry->GetType ());
+	/// 		serializer->Serialize (data, dbEntry);
+	/// 		PrivateFrame frame = PrivateFrame::Get (tag, L"org.MyProgram.DatabaseEntry", true);
+	/// 		frame.PrivateData = Encoding::UTF8->GetBytes (data->ToString ());
+	/// 	}
+	/// 	
+	/// 	static Object^ GetDatabaseEntry (Tag^ tag, Type^ type)
+	/// 	{
+	/// 		PrivateFrame^ frame = PrivateFrame::Get (tag, L"org.MyProgram.DatabaseEntry", false);
+	/// 		if (frame == null)
+	/// 			return null;
+	/// 	
+	/// 		XmlSerializer serializer = gcnew XmlSerializer (type);
+	/// 		return serializer->Deserialize (gcnew MemoryStream (frame->PrivateData));
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="VB">
+	/// Imports System
+	/// Imports System.IO
+	/// Imports System.Runtime.Serialization
+	/// Imports System.Text
+	/// Imports System.Xml.Serialization
+	/// Imports TagLib.Id3v2
+	///
+	/// Public Shared Class DbUtil
+	/// 	Public Shared Sub StoreDatabaseEntry (tag As Tag, dbEntry As ISerializable)
+	/// 		Dim data As New StringWriter (New StringBuilder ())
+	/// 		Dim serializer As New XmlSerializer (dbEntry.GetType ())
+	/// 		serializer.Serialize (data, dbEntry)
+	/// 		Dim frame As PrivateFrame = PrivateFrame.Get (tag, "org.MyProgram.DatabaseEntry", True)
+	/// 		frame.PrivateData = Encoding.UTF8.GetBytes (data.ToString ())
+	/// 	End Sub
+	/// 	
+	/// 	Public Shared Sub GetDatabaseEntry (tag As Tag, type As Type)
+	/// 		Dim frame As PrivateFrame = PrivateFrame.Get (tag, "org.MyProgram.DatabaseEntry", False)
+	/// 		If frame Is Nothing Then Return Nothing
+	/// 	
+	/// 		Dim serializer As XmlSerializer = New XmlSerializer (type)
+	/// 		Return serializer.Deserialize (New MemoryStream (frame.PrivateData))
+	/// 	End Sub
+	/// End Class
+	///    </code>
+	///    <code lang="Boo">
+	/// import System
+	/// import System.IO
+	/// import System.Runtime.Serialization
+	/// import System.Text
+	/// import System.Xml.Serialization
+	/// import TagLib.Id3v2
+	/// 
+	/// public static class DbUtil:
+	/// 	static def StoreDatabaseEntry (tag as Tag, dbEntry as ISerializable):
+	/// 		data as StringWriter = StringWriter (StringBuilder ())
+	/// 		serializer as XmlSerializer = XmlSerializer (dbEntry.GetType ())
+	/// 		serializer.Serialize (data, dbEntry)
+	/// 		frame as PrivateFrame = PrivateFrame.Get (tag, "org.MyProgram.DatabaseEntry", true)
+	/// 		frame.PrivateData = Encoding.UTF8.GetBytes (data.ToString ())
+	///	
+	/// 	static def GetDatabaseEntry (tag As Tag, type As Type):
+	/// 		frame as PrivateFrame = PrivateFrame.Get (tag, "org.MyProgram.DatabaseEntry", false)
+	/// 		if frame == null:
+	///			return null
+	/// 		
+	/// 		serializer as XmlSerializer = XmlSerializer (type)
+	/// 		return serializer.Deserialize (MemoryStream (frame.PrivateData))
+	///    </code>
+	/// </example>
+	public class PrivateFrame : Frame
+	{
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the owner of the current instance.
+		/// </summary>
+		private string owner = null;
+		
+		/// <summary>
+		///    Contains private data stored in the current instance.
+		/// </summary>
+		private ByteVector data  = null;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PrivateFrame" /> for a specified owner and data.
+		/// </summary>
+		/// <param name="owner">
+		///    A <see cref="string" /> containing the owner of the new
+		///    frame.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the data
+		///    for the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public PrivateFrame (string owner, ByteVector data)
+			: base (FrameType.PRIV, 4)
+		{
+			this.owner = owner;
+			this.data  = data;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PrivateFrame" /> without data for a specified
+		///    owner.
+		/// </summary>
+		/// <param name="owner">
+		///    A <see cref="string" /> containing the owner of the new
+		///    frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public PrivateFrame (string owner) : this (owner, null)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PrivateFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public PrivateFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PrivateFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal PrivateFrame (ByteVector data, int offset,
+		                                 FrameHeader header,
+		                                 byte version) : base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the owner of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the owner of the
+		///    current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a given owner per
+		///    tag.
+		/// </remarks>
+		public string Owner {
+			get {return owner;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the private data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> containing the private data
+		///    stored in the current instance.
+		/// </value>
+		public ByteVector PrivateData {
+			get {return data;}
+			set {data = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified private frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="owner">
+		///    A <see cref="string" /> specifying the owner to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="PrivateFrame" /> object containing the
+		///    matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static PrivateFrame Get (Tag tag, string owner,
+		                                bool create)
+		{
+			PrivateFrame priv;
+			
+			foreach (Frame frame in tag.GetFrames (FrameType.PRIV)) {
+				priv = frame as PrivateFrame;
+				if (priv != null && priv.Owner == owner)
+					return priv;
+			}
+			
+			if (!create)
+				return null;
+			
+			priv = new PrivateFrame (owner);
+			tag.AddFrame (priv);
+			return priv;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data, byte version)
+		{
+			if (data.Count < 1)
+				throw new CorruptFileException (
+					"A private frame must contain at least 1 byte.");
+			
+			ByteVectorCollection l = ByteVectorCollection.Split (
+				data,
+				ByteVector.TextDelimiter (StringType.Latin1),
+				1, 2);
+			
+			if (l.Count == 2) {
+				this.owner = l [0].ToString (StringType.Latin1);
+				this.data  = l [1];
+			}
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		/// <exception cref="NotImplementedException">
+		///    <paramref name="version" /> is less than 3. ID3v2.2 does
+		///    not support this frame.
+		/// </exception>
+		protected override ByteVector RenderFields (byte version)
+		{
+			if (version < 3)
+				throw new NotImplementedException ();
+			
+			ByteVector v = new ByteVector ();
+			
+			v.Add (ByteVector.FromString (owner, StringType.Latin1));
+			v.Add (ByteVector.TextDelimiter (StringType.Latin1));
+			v.Add (data);
+			
+			return v;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			PrivateFrame frame = new PrivateFrame (owner);
+			if (data != null)
+				frame.data = new ByteVector (data);
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/RelativeVolumeFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/RelativeVolumeFrame.cs
new file mode 100644
index 0000000..d67fea5
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/RelativeVolumeFrame.cs
@@ -0,0 +1,610 @@
+//
+// RelativeVolumeFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   textidentificationframe.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2004 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System.Collections.Generic;
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    Specified the type of channel data to get from or set to a
+	///    <see cref="RelativeVolumeFrame" /> object.
+	/// </summary>
+	public enum ChannelType {
+		/// <summary>
+		///    The channel data is for some other speaker.
+		/// </summary>
+		Other = 0x00,
+		
+		/// <summary>
+		///    The channel data is for the master volume.
+		/// </summary>
+		MasterVolume = 0x01,
+		
+		/// <summary>
+		///    The channel data is for the front right speaker.
+		/// </summary>
+		FrontRight = 0x02,
+		
+		/// <summary>
+		///    The channel data is for the front left speaker.
+		/// </summary>
+		FrontLeft = 0x03,
+		
+		/// <summary>
+		///    The channel data is for the back right speaker.
+		/// </summary>
+		BackRight = 0x04,
+		
+		/// <summary>
+		///    The channel data is for the back left speaker.
+		/// </summary>
+		BackLeft = 0x05,
+		
+		/// <summary>
+		///    The channel data is for the front center speaker.
+		/// </summary>
+		FrontCentre = 0x06,
+		
+		/// <summary>
+		///    The channel data is for the back center speaker.
+		/// </summary>
+		BackCentre = 0x07,
+		
+		/// <summary>
+		///    The channel data is for the subwoofer.
+		/// </summary>
+		Subwoofer = 0x08
+	}
+	
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Relative Volume (RVA2) Frames.
+	/// </summary>
+	public class RelativeVolumeFrame : Frame
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the frame identification.
+		/// </summary>
+		private string identification = null;
+		
+		/// <summary>
+		///    Contains the channel data.
+		/// </summary>
+		private ChannelData [] channels = new ChannelData [9];
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="RelativeVolumeFrame" /> with a specified
+		///    identifier.
+		/// </summary>
+		/// <param name="identification">
+		///    A <see cref="string" /> object containing the
+		///    identification to use for the new frame.
+		/// </param>
+		public RelativeVolumeFrame (string identification)
+			: base (FrameType.RVA2, 4)
+		{
+			this.identification = identification;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="RelativeVolumeFrame" /> by reading its raw data in
+		///    a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public RelativeVolumeFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="RelativeVolumeFrame" /> by reading its raw data in
+		///    a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal RelativeVolumeFrame (ByteVector data,
+		                                        int offset,
+		                                        FrameHeader header,
+		                                        byte version)
+			: base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the identification used for the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the
+		///    identification used for the current instance.
+		/// </value>
+		public string Identification {
+			get {return identification;}
+		}
+		
+		/// <summary>
+		///    Gets a list of the channels in the current instance that
+		///    contain a value.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ChannelType[]" /> containing the channels
+		///    which have a value set in the current instance.
+		/// </value>
+		public ChannelType [] Channels {
+			get {
+				List<ChannelType> types = new List<ChannelType> ();
+				for (int i = 0; i < 9; i ++)
+					if (channels [i].IsSet)
+						types.Add ((ChannelType) i);
+				return types.ToArray ();
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Creates a text description of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> object containing a description
+		///    of the current instance.
+		/// </returns>
+		public override string ToString ()
+		{
+			return identification;
+		}
+		
+		/// <summary>
+		///    Gets the volume adjustment index for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to get the value for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="short" /> value containing the volume
+		///    adjustment index.
+		/// </returns>
+		/// <remarks>
+		///    The volume adjustment index is simply the volume
+		///    adjustment multiplied by 512.
+		/// </remarks>
+		/// <seealso cref="SetVolumeAdjustmentIndex"/>
+		/// <seealso cref="GetVolumeAdjustment"/>
+		public short GetVolumeAdjustmentIndex (ChannelType type)
+		{
+			return channels [(int) type].VolumeAdjustmentIndex;
+		}
+		
+		/// <summary>
+		///    Sets the volume adjustment index for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to set the value for.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="short" /> value containing the volume
+		///    adjustment index.
+		/// </param>
+		/// <seealso cref="GetVolumeAdjustmentIndex"/>
+		/// <seealso cref="SetVolumeAdjustment"/>
+		public void SetVolumeAdjustmentIndex (ChannelType type,
+		                                      short index)
+		{
+			channels [(int) type].VolumeAdjustmentIndex = index;
+		}
+		
+		/// <summary>
+		///    Gets the volume adjustment for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to get the value for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="float" /> value containing the volume
+		///    adjustment in decibles.
+		/// </returns>
+		/// <remarks>
+		///    The value can be between -64dB and +64dB.
+		/// </remarks>
+		/// <seealso cref="SetVolumeAdjustment"/>
+		/// <seealso cref="GetVolumeAdjustmentIndex"/>
+		public float GetVolumeAdjustment (ChannelType type)
+		{
+			return channels [(int) type].VolumeAdjustment;
+		}
+		
+		/// <summary>
+		///    Sets the volume adjustment for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to set the value for.
+		/// </param>
+		/// <param name="adjustment">
+		///    A <see cref="float" /> value containing the volume
+		///    adjustment in decibles.
+		/// </param>
+		/// <remarks>
+		///    The value can be between -64dB and +64dB.
+		/// </remarks>
+		/// <seealso cref="GetVolumeAdjustment"/>
+		/// <seealso cref="SetVolumeAdjustmentIndex"/>
+		public void SetVolumeAdjustment (ChannelType type,
+		                                 float adjustment)
+		{
+			channels [(int) type].VolumeAdjustment = adjustment;
+		}
+		
+		/// <summary>
+		///    Gets the peak volume index for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to get the value for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ulong" /> value containing the peak volume
+		///    index.
+		/// </returns>
+		/// <remarks>
+		///    The peak volume index is simply the peak volume
+		///    multiplied by 512.
+		/// </remarks>
+		/// <seealso cref="SetPeakVolumeIndex"/>
+		/// <seealso cref="GetPeakVolume"/>
+		public ulong GetPeakVolumeIndex (ChannelType type)
+		{
+			return channels [(int) type].PeakVolumeIndex;
+		}
+		
+		/// <summary>
+		///    Sets the peak volume index for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to set the value for.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="ulong" /> value containing the peak volume
+		///    index.
+		/// </param>
+		/// <remarks>
+		///    The peak volume index is simply the peak volume
+		///    multiplied by 512.
+		/// </remarks>
+		/// <seealso cref="GetPeakVolumeIndex"/>
+		/// <seealso cref="SetPeakVolume"/>
+		public void SetPeakVolumeIndex (ChannelType type, ulong index)
+		{
+			channels [(int) type].PeakVolumeIndex = index;
+		}
+		
+		/// <summary>
+		///    Gets the peak volume for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to get the value for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="double" /> value containing the peak volume.
+		/// </returns>
+		/// <seealso cref="SetPeakVolume"/>
+		/// <seealso cref="GetPeakVolumeIndex"/>
+		public double GetPeakVolume (ChannelType type)
+		{
+			return channels [(int) type].PeakVolume;
+		}
+		
+		/// <summary>
+		///    Sets the peak volume for a specified channel.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ChannelType" /> value specifying which
+		///    channel to set the value for.
+		/// </param>
+		/// <param name="peak">
+		///    A <see cref="double" /> value containing the peak volume.
+		/// </param>
+		/// <seealso cref="GetPeakVolume"/>
+		/// <seealso cref="SetPeakVolumeIndex"/>
+		public void SetPeakVolume (ChannelType type, double peak)
+		{
+			channels [(int) type].PeakVolume = peak;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified volume adjustment frame from the
+		///    specified tag, optionally creating it if it does not
+		///    exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="identification">
+		///    A <see cref="string" /> specifying the identification to
+		///    match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="RelativeVolumeFrame" /> object containing
+		///    the matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static RelativeVolumeFrame Get (Tag tag,
+		                                       string identification,
+		                                       bool create)
+		{
+			RelativeVolumeFrame rva2;
+			foreach (Frame frame in tag.GetFrames (FrameType.RVA2)) {
+				rva2 = frame as RelativeVolumeFrame;
+				
+				if (rva2 == null)
+					continue;
+				
+				if (rva2.Identification != identification)
+					continue;
+				
+				return rva2;
+			}
+			
+			if (!create)
+				return null;
+			
+			rva2 = new RelativeVolumeFrame (identification);
+			tag.AddFrame (rva2);
+			return rva2;
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Properties
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 5 bytes.
+		/// </exception>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			int pos = data.Find (ByteVector.TextDelimiter (
+				StringType.Latin1));
+			if (pos < 0)
+				return;
+			
+			identification = data.ToString (StringType.Latin1, 0,
+				pos++);
+			
+			// Each channel is at least 4 bytes.
+			
+			while (pos <= data.Count - 4) {
+				int type = data [pos++];
+				
+				unchecked {
+					channels [type].VolumeAdjustmentIndex =
+						(short) data.Mid (pos,
+							2).ToUShort ();
+				}
+				pos += 2;
+				
+				int bytes = BitsToBytes (data [pos++]);
+				
+				if (data.Count < pos + bytes)
+					break;
+				
+				channels [type].PeakVolumeIndex = data.Mid (pos,
+					bytes).ToULong ();
+				pos += bytes;
+			}
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			ByteVector data = new ByteVector ();
+			data.Add (ByteVector.FromString (identification,
+				StringType.Latin1));
+			data.Add (ByteVector.TextDelimiter(StringType.Latin1));
+			
+			for (byte i = 0; i < 9; i ++) {
+				if (!channels [i].IsSet)
+					continue;
+				
+				data.Add (i);
+				unchecked {
+					data.Add (ByteVector.FromUShort (
+						(ushort) channels [i]
+							.VolumeAdjustmentIndex));
+				}
+				
+				byte bits = 0;
+				
+				for (byte j = 0; j < 64; j ++)
+					if ((channels [i].PeakVolumeIndex &
+						(1UL << j)) != 0)
+						bits = (byte)(j + 1);
+				
+				data.Add (bits);
+				
+				if (bits > 0)
+					data.Add (ByteVector.FromULong (
+						channels [i].PeakVolumeIndex)
+							.Mid (8 - BitsToBytes (bits)));
+			}
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			RelativeVolumeFrame frame =
+				new RelativeVolumeFrame (identification);
+			for (int i = 0; i < 9; i ++)
+				frame.channels [i] = channels [i];
+			return frame;
+		}
+		
+#endregion
+		
+		
+		
+#region Private Static Methods
+		
+		private static int BitsToBytes (int i)
+		{
+			return i % 8 == 0 ? i / 8 : (i - i % 8) / 8 + 1;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Classes
+		
+		private struct ChannelData
+		{
+			public short VolumeAdjustmentIndex;
+			public ulong PeakVolumeIndex;
+			
+			public bool IsSet {
+				get {
+					return VolumeAdjustmentIndex != 0 ||
+						PeakVolumeIndex != 0;
+				}
+			}
+			
+			public float VolumeAdjustment {
+				get {return VolumeAdjustmentIndex / 512f;}
+				set {
+					VolumeAdjustmentIndex =
+						(short) (value * 512f);
+				}
+			}
+			
+			public double PeakVolume {
+				get {return PeakVolumeIndex / 512.0;}
+				set {PeakVolumeIndex = (ulong) (value * 512.0);}
+			}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/SynchronizedLyricsFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/SynchronizedLyricsFrame.cs
new file mode 100644
index 0000000..b6cca18
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/SynchronizedLyricsFrame.cs
@@ -0,0 +1,722 @@
+//
+// SynchronizedLyricsFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    Specifies the timestamp format used by a <see
+	///    cref="SynchronisedLyricsFrame" />.
+	/// </summary>
+	public enum TimestampFormat
+	{
+		/// <summary>
+		///    The timestamp is of unknown format.
+		/// </summary>
+		Unknown              = 0x00,
+		
+		/// <summary>
+		///    The timestamp represents the number of MPEG frames since
+		///    the beginning of the audio stream.
+		/// </summary>
+		AbsoluteMpegFrames   = 0x01,
+		
+		/// <summary>
+		///    The timestamp represents the number of milliseconds since
+		///    the beginning of the audio stream.
+		/// </summary>
+		AbsoluteMilliseconds = 0x02
+	}
+	
+	/// <summary>
+	///    Specifies the type of text contained in a <see
+	///    cref="SynchronisedLyricsFrame" />.
+	/// </summary>
+	public enum SynchedTextType
+	{
+		/// <summary>
+		///    The text is some other type of text.
+		/// </summary>
+		Other             = 0x00,
+		
+		/// <summary>
+		///    The text contains lyrical data.
+		/// </summary>
+		Lyrics            = 0x01,
+		
+		/// <summary>
+		///    The text contains a transcription.
+		/// </summary>
+		TextTranscription = 0x02,
+		
+		/// <summary>
+		///    The text lists the movements in the piece.
+		/// </summary>
+		Movement          = 0x03,
+		
+		/// <summary>
+		///    The text describes events that occur.
+		/// </summary>
+		Events            = 0x04,
+		
+		/// <summary>
+		///    The text contains chord changes that occur in the music.
+		/// </summary>
+		Chord             = 0x05,
+		
+		/// <summary>
+		///    The text contains trivia or "pop up" information about
+		///    the media.
+		/// </summary>
+		Trivia            = 0x06,
+		
+		/// <summary>
+		///    The text contains URL's for relevant webpages.
+		/// </summary>
+		WebpageUrls       = 0x07,
+		
+		/// <summary>
+		///     The text contains URL's for relevant images.
+		/// </summary>
+		ImageUrls         = 0x08
+	}
+	
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Synchronised Lyrics and Text (SYLT) Frames.
+	/// </summary>
+	public class SynchronisedLyricsFrame : Frame
+	{
+#region Private Properties
+		
+		/// <summary>
+		///    Contains the text encoding to use when rendering the
+		///    current instance.
+		/// </summary>
+		private StringType encoding = Tag.DefaultEncoding;
+		
+		/// <summary>
+		///    Contains the ISO-639-2 language code.
+		/// </summary>
+		private string language = null;
+		
+		/// <summary>
+		///    Contains the description.
+		/// </summary>
+		private string description = null;
+		
+		/// <summary>
+		///    Contains the timestamp format.
+		/// </summary>
+		private TimestampFormat timestamp_format =
+			TimestampFormat.Unknown;
+		
+		/// <summary>
+		///    Contains the text type.
+		/// </summary>
+		private SynchedTextType lyrics_type = SynchedTextType.Other;
+		
+		/// <summary>
+		///    Contains the text.
+		/// </summary>
+		private SynchedText [] text = new SynchedText [0];
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="SynchronisedLyricsFrame" /> with a specified
+		///    description, ISO-639-2 language code, text type, and text
+		///    encoding.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> object containing the description
+		///    of the new instnace.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> object containing the ISO-639-2
+		///    language code of the new instance.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="SynchedTextType" /> containing the type of
+		///    text to be stored in the new instance.
+		/// </param>
+		/// <param name="encoding">
+		///    A <see cref="StringType" /> containing the text encoding
+		///    to use when rendering the new instance.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public SynchronisedLyricsFrame (string description,
+		                                string language,
+		                                SynchedTextType type,
+		                                StringType encoding)
+			: base (FrameType.SYLT, 4)
+		{
+			this.encoding = encoding;
+			this.language = language;
+			this.description = description;
+			this.lyrics_type = type;
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="SynchronisedLyricsFrame" /> with a specified
+		///    description, ISO-639-2 language code, and text type.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> object containing the description
+		///    of the new instnace.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> object containing the ISO-639-2
+		///    language code of the new instance.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="SynchedTextType" /> containing the type of
+		///    text to be stored in the new instance.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public SynchronisedLyricsFrame (string description,
+		                                string language,
+		                                SynchedTextType type)
+			: this (description, language, type,
+				TagLib.Id3v2.Tag.DefaultEncoding)
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="SynchronisedLyricsFrame" /> by reading its raw data
+		///    in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new instance.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public SynchronisedLyricsFrame (ByteVector data, byte version)
+			: base(data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="SynchronisedLyricsFrame" /> by reading its raw data
+		///    in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new instance.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal SynchronisedLyricsFrame (ByteVector data,
+		                                            int offset,
+		                                            FrameHeader header,
+		                                            byte version)
+			: base (header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the text encoding to use when storing the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the text encoding to
+		///    use when storing the current instance.
+		/// </value>
+		/// <remarks>
+		///    This encoding is overridden when rendering if <see
+		///    cref="Tag.ForceDefaultEncoding" /> is <see
+		///    langword="true" /> or the render version does not support
+		///    it.
+		/// </remarks>
+		public StringType TextEncoding {
+			get {return encoding;}
+			set {encoding = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the ISO-639-2 language code stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description, type, and ISO-639-2 language code per tag.
+		/// </remarks>
+		public string Language {
+			get {
+				return (language != null && language.Length > 2)
+					? language.Substring (0, 3) : "XXX";
+			}
+			set {language = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the description stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the description
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description, type, and ISO-639-2 language code per tag.
+		/// </remarks>
+		public string Description {
+			get {return description;}
+			set {description = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the timestamp format used by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimestampFormat" /> value describing the
+		///    timestamp format used by the current instance.
+		/// </value>
+		public TimestampFormat Format {
+			get {return timestamp_format;}
+			set {timestamp_format = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the type of text contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimestampFormat" /> value describing the
+		///    type of text contained in the current instance.
+		/// </value>
+		public SynchedTextType Type {
+			get {return lyrics_type;}
+			set {lyrics_type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the text contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="SynchedText[]" /> containing the text
+		///    contained in the current instance.
+		/// </value>
+		public SynchedText [] Text {
+			get {return text;}
+			set {
+				text = value == null ? new SynchedText [0] :
+					value;
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified lyrics frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> object specifying the description
+		///    to match.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> object specifying the ISO-639-2
+		///    language code to match.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="SynchedTextType" /> value specifying the
+		///    text type to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="SynchronisedLyricsFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found and <paramref name="create" /> is
+		///    <see langword="false" />.
+		/// </returns>
+		public static SynchronisedLyricsFrame Get (Tag tag,
+		                                           string description,
+		                                           string language,
+		                                           SynchedTextType type,
+		                                           bool create)
+		{
+			foreach (Frame f in tag) {
+				SynchronisedLyricsFrame lyr =
+					f as SynchronisedLyricsFrame;
+				
+				if (lyr == null)
+					continue;
+				
+				if (lyr.Description == description &&
+					(language == null ||
+						language == lyr.Language) &&
+					type == lyr.Type)
+					return lyr;
+			}
+
+			if (!create)
+				return null;
+
+			SynchronisedLyricsFrame frame =
+				new SynchronisedLyricsFrame (description,
+					language, type);
+			tag.AddFrame (frame);
+			return frame;
+		}
+
+		/// <summary>
+		///    Gets a specified lyrics frame from the specified tag,
+		///    trying to to match the description and language but
+		///    accepting an incomplete match.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> object specifying the description
+		///    to match.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> object specifying the ISO-639-2
+		///    language code to match.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="SynchedTextType" /> value specifying the
+		///    text type to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="SynchronisedLyricsFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found.
+		/// </returns>
+		/// <remarks>
+		///    <para>The method tries matching with the following order
+		///    of precidence:</para>
+		///    <list type="number">
+		///       <item><term>The first frame with a matching
+		///       description, language, and type.</term></item>
+		///       <item><term>The first frame with a matching
+		///       description and language.</term></item>
+		///       <item><term>The first frame with a matching
+		///       language.</term></item>
+		///       <item><term>The first frame with a matching
+		///       description.</term></item>
+		///       <item><term>The first frame with a matching
+		///       type.</term></item>
+		///       <item><term>The first frame.</term></item>
+		///    </list>
+		/// </remarks>
+		public static SynchronisedLyricsFrame GetPreferred (Tag tag,
+		                                                    string description,
+		                                                    string language,
+		                                                    SynchedTextType type)
+		{
+			// This is weird, so bear with me. The best thing we can
+			// have is something straightforward and in our own
+			// language. If it has a description, then it is
+			// probably used for something other than an actual
+			// comment. If that doesn't work, we'd still rather have
+			// something in our language than something in another.
+			// After that all we have left are things in other
+			// languages, so we'd rather have one with actual
+			// content, so we try to get one with no description
+			// first.
+
+			int best_value = -1;
+			SynchronisedLyricsFrame best_frame = null;
+			
+			foreach (Frame f in tag) {
+				SynchronisedLyricsFrame cf =
+					f as SynchronisedLyricsFrame;
+				
+				if (cf == null)
+					continue;
+				
+				int value = 0;
+				if (cf.Language == language)
+					value += 4;
+				if (cf.Description == description)
+					value += 2;
+				if (cf.Type == type)
+					value += 1;
+				
+				if (value == 7)
+					return cf;
+				
+				if (value <= best_value)
+					continue;
+				
+				best_value = value;
+				best_frame = cf;
+			}
+			
+			return best_frame;
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			if (data.Count < 6)
+				throw new CorruptFileException (
+					"Not enough bytes in field.");
+
+			encoding = (StringType) data [0];
+			language = data.ToString (StringType.Latin1, 1, 3);
+			timestamp_format = (TimestampFormat) data [4];
+			lyrics_type = (SynchedTextType) data [5];
+
+			ByteVector delim = ByteVector.TextDelimiter (
+				encoding);
+			int delim_index = data.Find (delim, 6, delim.Count);
+
+			if (delim_index < 0)
+				throw new CorruptFileException (
+					"Text delimiter expected.");
+
+			description = data.ToString (encoding, 6,
+				delim_index - 6);
+
+			int offset = delim_index + delim.Count;
+			List<SynchedText> l = new List<SynchedText> ();
+
+			while (offset + delim.Count + 4 < data.Count) {
+				delim_index = data.Find (delim, offset,
+					delim.Count);
+				
+				if (delim_index < offset)
+					throw new CorruptFileException (
+						"Text delimiter expected.");
+				
+				string text = data.ToString (encoding, offset,
+					delim_index - offset);
+				offset = delim_index + delim.Count;
+				
+				if (offset + 4 > data.Count)
+					break;
+				
+				l.Add (new SynchedText (data.Mid (offset, 4)
+					.ToUInt (), text));
+				offset += 4;
+			}
+			
+			this.text = l.ToArray ();
+		}
+
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			StringType encoding = CorrectEncoding(TextEncoding,
+				version);
+			ByteVector delim = ByteVector.TextDelimiter (encoding);
+			ByteVector v = new ByteVector ();
+			
+			v.Add ((byte) encoding);
+			v.Add (ByteVector.FromString (Language,
+				StringType.Latin1));
+			v.Add ((byte) timestamp_format);
+			v.Add ((byte) lyrics_type);
+			v.Add (ByteVector.FromString (description, encoding));
+			v.Add (delim);
+			
+			foreach (SynchedText t in text) {
+				v.Add (ByteVector.FromString (t.Text, encoding));
+				v.Add (delim);
+				v.Add (ByteVector.FromUInt ((uint)t.Time));
+			}
+			
+			return v;
+		}
+		
+		#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			SynchronisedLyricsFrame frame =
+				new SynchronisedLyricsFrame (description,
+					language, lyrics_type, encoding);
+			frame.timestamp_format = timestamp_format;
+			frame.text = (SynchedText[]) text.Clone ();
+			return frame;
+		}
+
+#endregion
+	}
+	
+	
+	/// <summary>
+	///    This structure contains a single entry in a <see
+	///    cref="SynchronisedLyricsFrame" /> object.
+	/// </summary>
+	public struct SynchedText
+	{
+		/// <summary>
+		///    Contains the time offset.
+		/// </summary>
+		private long time;
+		
+		/// <summary>
+		///    Contains the text.
+		/// </summary>
+		private string text;
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="SynchedText" /> with a specified time and text.
+		/// </summary>
+		/// <param name="time">
+		///    A <see cref="long" /> value representing an amount of
+		///    time in a format define in the class using it. The
+		///    specific format is specified in <see
+		///    cref="SynchronisedLyricsFrame.Format" />.
+		/// </param>
+		/// <param name="text">
+		///    A <see cref="string" /> object containing the text
+		///    for the point in time.
+		/// </param>
+		public SynchedText (long time, string text)
+		{
+			this.time = time;
+			this.text = text;
+		}
+		
+		/// <summary>
+		///    Gets and sets the time offset of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value representing an amount of
+		///    time in a format define in the class using it. The
+		///    specific format is specified in <see
+		///    cref="SynchronisedLyricsFrame.Format" />.
+		/// </value>
+		public long Time {
+			get {return time;}
+			set {time = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the text for the point in time represented
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the text
+		///    for the point in time.
+		/// </value>
+		public string Text {
+			get {return text;}
+			set {text = value;}
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/TermsOfUseFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/TermsOfUseFrame.cs
new file mode 100644
index 0000000..6c08008
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/TermsOfUseFrame.cs
@@ -0,0 +1,390 @@
+//
+// TermsOfUseFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Terms of Use (USER) Frames.
+	/// </summary>
+	/// <remarks>
+	///    This frame contains license text or restrictions on the use of a
+	///    media file.
+	/// </remarks>
+	public class TermsOfUseFrame : Frame
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the text encoding to use when rendering the
+		///    current instance.
+		/// </summary>
+		private StringType encoding = Tag.DefaultEncoding;
+		
+		/// <summary>
+		///    Contains the ISO-639-2 language code of the current
+		///    instance.
+		/// </summary>
+		private string language = null;
+		
+		/// <summary>
+		///    Contains the text in the current instance.
+		/// </summary>
+		private string text = null;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and intializes a new instance of <see
+		///    cref="TermsOfUseFrame" /> with a specified language and
+		///    encoding.
+		/// </summary>
+		/// <param name="language">
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code of the new frame.
+		/// </param>
+		/// <param name="encoding">
+		///    A <see cref="StringType" /> containing the text encoding
+		///    to use when rendering the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public TermsOfUseFrame (string language, StringType encoding)
+			: base (FrameType.USER, 4)
+		{
+			this.encoding = encoding;
+			this.language = language;
+		}
+		
+		/// <summary>
+		///    Constructs and intializes a new instance of <see
+		///    cref="TermsOfUseFrame" /> with a specified language.
+		/// </summary>
+		/// <param name="language">
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code of the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public TermsOfUseFrame (string language)
+			: base (FrameType.USER, 4)
+		{
+			this.language = language;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="TermsOfUseFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public TermsOfUseFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="TermsOfUseFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal TermsOfUseFrame (ByteVector data, int offset,
+		                                    FrameHeader header,
+		                                    byte version)
+			: base (header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the text encoding to use when storing the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the text encoding to
+		///    use when storing the current instance.
+		/// </value>
+		/// <remarks>
+		///    This encoding is overridden when rendering if <see
+		///    cref="Tag.ForceDefaultEncoding" /> is <see
+		///    langword="true" /> or the render version does not support
+		///    it.
+		/// </remarks>
+		public StringType TextEncoding {
+			get {return encoding;}
+			set {encoding = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the ISO-639-2 language code stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one file with a matching 
+		///    ISO-639-2 language code per tag.
+		/// </remarks>
+		public string Language {
+			get {
+				return (language != null && language.Length > 2)
+					? language.Substring (0, 3) : "XXX";
+			}
+			set {language = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the terms of use stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the terms of
+		///    use.
+		/// </value>
+		public string Text {
+			get {return text;}
+			set {text = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> containing the terms of use.
+		/// </returns>
+		public override string ToString ()
+		{
+			return text;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified terms of use frame from the specified
+		///    tag, optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> specifying the ISO-639-2 language
+		///   code to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TermsOfUseFrame" /> object containing the
+		///    matching frame, or <see langword="null" /> if a match
+		///    wasn't found and <paramref name="create" /> is <see
+		///    langword="false" />.
+		/// </returns>
+		public static TermsOfUseFrame Get (Tag tag, string language,
+		                                   bool create)
+		{
+			foreach (Frame f in tag.GetFrames (FrameType.USER)) {
+				TermsOfUseFrame cf = f as TermsOfUseFrame;
+				
+				if (cf != null && (language == null ||
+					language == cf.Language))
+					return cf;
+			}
+			
+			if (!create)
+				return null;
+			
+			TermsOfUseFrame frame = new TermsOfUseFrame (language);
+			tag.AddFrame (frame);
+			return frame;
+		}
+		
+		/// <summary>
+		///    Gets a specified terms of use frame from the specified
+		///    tag, trying to to match the language but accepting one
+		///    with a different language if a match was not found.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> specifying the ISO-639-2 language
+		///   code to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TermsOfUseFrame" /> object containing the
+		///    matching frame, or <see langword="null" /> if a match
+		///    wasn't found.
+		/// </returns>
+		public static TermsOfUseFrame GetPreferred (Tag tag,
+		                                            string language)
+		{
+			TermsOfUseFrame best = null;
+			foreach (Frame f in tag.GetFrames (FrameType.USER)) {
+				TermsOfUseFrame cf = f as TermsOfUseFrame;
+				if (cf == null)
+					continue;
+				
+				if (cf.Language == language)
+					return cf;
+				
+				if (best == null)
+					best = cf;
+			}
+			
+			return best;
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			if (data.Count < 4)
+				throw new CorruptFileException (
+					"Not enough bytes in field.");
+			
+			encoding = (StringType) data [0];
+			language = data.ToString (StringType.Latin1, 1, 3);
+			text = data.ToString (encoding, 4, data.Count - 4);
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			StringType encoding = CorrectEncoding (TextEncoding,
+				version);
+			ByteVector v = new ByteVector ();
+			
+			v.Add ((byte) encoding);
+			v.Add (ByteVector.FromString (Language,
+				StringType.Latin1));
+			v.Add (ByteVector.FromString (text, encoding));
+			
+			return v;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			TermsOfUseFrame frame = new TermsOfUseFrame (language, encoding);
+			frame.text = text;
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/TextIdentificationFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/TextIdentificationFrame.cs
new file mode 100644
index 0000000..6ccde1b
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/TextIdentificationFrame.cs
@@ -0,0 +1,1344 @@
+//
+// TextInformationFrame.cs: Provides support ID3v2 Text Information Frames
+// (Section 4.2), covering "T000" to "TZZZ", excluding "TXXX".
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   textidentificationframe.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This class extends <see cref="Frame" /> to provide support ID3v2
+	///    Text Information Frames (Section 4.2), covering "<c>T000</c>" to
+	///    "<c>TZZZ</c>", excluding "<c>TXXX</c>".
+	/// </summary>
+	/// <remarks>
+	///    <para>Text Information Frames contain the most commonly used
+	///    values in tagging, including the artist, the track name, and just
+	///    about any value that can be expressed as text.</para>
+	///    <para>The following table contains types and descriptions as
+	///    found in the ID3 2.4.0 native frames specification. (Copyright
+	///    (C) Martin Nilsson 2000.)</para>
+	///    
+	///    <list type="table">
+	///       <listheader>
+	///          <term>ID</term>
+	///          <description>Description</description>
+	///       </listheader>
+	///       <item>
+	///          <term>TIT1</term>
+	///          <description>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").</description>
+	///       </item>
+	///       <item>
+	///          <term>TIT2</term>
+	///          <description>The 'Title/Songname/Content description' frame
+	///          is the actual name of the piece (e.g. "Adagio", "Hurricane
+	///          Donna").</description>
+	///       </item>
+	///       <item>
+	///          <term>TIT3</term>
+	///          <description>The 'Subtitle/Description refinement' frame is
+	///          used for information directly related to the contents title
+	///          (e.g. "Op. 16" or "Performed live at
+	///          Wembley").</description>
+	///       </item>
+	///       <item>
+	///          <term>TALB</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TOAL</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TRCK</term>
+	///          <description>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".</description>
+	///       </item>
+	///       <item>
+	///          <term>TPOS</term>
+	///          <description>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".</description>
+	///       </item>
+	///       <item>
+	///          <term>TSST</term>
+	///          <description>The 'Set subtitle' frame is intended for the
+	///          subtitle of the part of a set this track belongs
+	///          to.</description>
+	///       </item>
+	///       <item>
+	///          <term>TSRC</term>
+	///          <description>The 'ISRC' frame should contain the
+	///          International Standard Recording Code [ISRC] (12
+	///          characters).</description>
+	///       </item>
+	///       <item>
+	///          <term>TPE1</term>
+	///          <description>The
+	///          'Lead artist/Lead performer/Soloist/Performing group' is
+	///          used for the main artist.</description>
+	///       </item>
+	///       <item>
+	///          <term>TPE2</term>
+	///          <description>The 'Band/Orchestra/Accompaniment' frame is
+	///          used for additional information about the performers in the
+	///          recording.</description>
+	///       </item>
+	///       <item>
+	///          <term>TPE3</term>
+	///          <description>The 'Conductor' frame is used for the name of
+	///          the conductor.</description>
+	///       </item>
+	///       <item>
+	///          <term>TPE4</term>
+	///          <description>The 'Interpreted, remixed, or otherwise
+	///          modified by' frame contains more information about the
+	///          people behind a remix and similar interpretations of
+	///          another existing piece.</description>
+	///       </item>
+	///       <item>
+	///          <term>TOPE</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TEXT</term>
+	///          <description>The 'Lyricist/Text writer' frame is intended
+	///          for the writer of the text or lyrics in the
+	///          recording.</description>
+	///       </item>
+	///       <item>
+	///          <term>TOLY</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TCOM</term>
+	///          <description>The 'Composer' frame is intended for the name
+	///          of the composer.</description>
+	///       </item>
+	///       <item>
+	///          <term>TMCL</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TIPL</term>
+	///          <description>The 'Involved people list' is very similar to
+	///          the musician credits list, but maps between functions, like
+	///          producer, and names.</description>
+	///       </item>
+	///       <item>
+	///          <term>TENC</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TBPM</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TLEN</term>
+	///          <description>The 'Length' frame contains the length of the
+	///          audio file in milliseconds, represented as a numeric
+	///          string.</description>
+	///       </item>
+	///       <item>
+	///          <term>TKEY</term>
+	///          <description>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". Off key is represented with
+	///          an "o" only.</description>
+	///       </item>
+	///       <item>
+	///          <term>TLAN</term>
+	///          <description>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. If more than one language is used
+	///          in the text their language codes should follow according to
+	///          the amount of their usage.</description>
+	///       </item>
+	///       <item>
+	///          <term>TCON</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TFLT</term>
+	///          <description>The 'File type' frame indicates which type of
+	///          audio this tag defines. (See the specification for more
+	///          details.)</description>
+	///       </item>
+	///       <item>
+	///          <term>TMED</term>
+	///          <description>The 'Media type' frame describes from which
+	///          media the sound originated. (See the specification for more
+	///          details.)</description>
+	///       </item>
+	///       <item>
+	///          <term>TMOO</term>
+	///          <description>The 'Mood' frame is intended to reflect the
+	///          mood of the audio with a few keywords, e.g. "Romantic" or
+	///          "Sad".</description>
+	///       </item>
+	///       <item>
+	///          <term>TCOP</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TPRO</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TPUB</term>
+	///          <description>The 'Publisher' frame simply contains the name
+	///          of the label or publisher.</description>
+	///       </item>
+	///       <item>
+	///          <term>TOWN</term>
+	///          <description>The 'File owner/licensee' frame contains the
+	///          name of the owner or licensee of the file and it's
+	///          contents.</description>
+	///       </item>
+	///       <item>
+	///          <term>TRSN</term>
+	///          <description>The 'Internet radio station name' frame
+	///          contains the name of the internet radio station from which
+	///          the audio is streamed.</description>
+	///       </item>
+	///       <item>
+	///          <term>TRSO</term>
+	///          <description>The 'Internet radio station owner' frame
+	///          contains the name of the owner of the internet radio
+	///          station from which the audio is streamed.</description>
+	///       </item>
+	///       <item>
+	///          <term>TOFN</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TDLY</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TDEN</term>
+	///          <description>The 'Encoding time' frame contains a timestamp
+	///          describing when the audio was encoded. Timestamp format is
+	///          described in the ID3v2 structure document.</description>
+	///       </item>
+	///       <item>
+	///          <term>TDOR</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TDRC</term>
+	///          <description>The 'Recording time' frame contains a
+	///          timestamp describing when the audio was recorded. Timestamp
+	///          format is described in the ID3v2 structure
+	///          document.</description>
+	///       </item>
+	///       <item>
+	///          <term>TDRL</term>
+	///          <description>The 'Release time' frame contains a timestamp
+	///          describing when the audio was first released. Timestamp
+	///          format is described in the ID3v2 structure
+	///          document.</description>
+	///       </item>
+	///       <item>
+	///          <term>TDTG</term>
+	///          <description>The 'Tagging time' frame contains a timestamp
+	///          describing then the audio was tagged. Timestamp format is
+	///          described in the ID3v2 structure document.</description>
+	///       </item>
+	///       <item>
+	///          <term>TSSE</term>
+	///          <description>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.</description>
+	///       </item>
+	///       <item>
+	///          <term>TSOA</term>
+	///          <description>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".</description>
+	///       </item>
+	///       <item>
+	///          <term>TSOP</term>
+	///          <description>The 'Performer sort order' frame defines a
+	///          string which should be used instead of the performer (TPE2)
+	///          for sorting purposes.</description>
+	///       </item>
+	///       <item>
+	///          <term>TSOT</term>
+	///          <description>The 'Title sort order' frame defines a string
+	///          which should be used instead of the title (TIT2) for
+	///          sorting purposes.</description>
+	///       </item>
+	///    </list>
+	/// </remarks>
+	public class TextInformationFrame : Frame
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the encoding to use for the text.
+		/// </summary>
+		private StringType encoding = Id3v2.Tag.DefaultEncoding;
+		
+		/// <summary>
+		///    Contains the text fields.
+		/// </summary>
+		private string [] text_fields = new string [0];
+		
+		/// <summary>
+		///    Contains the raw data from the frame, or <see
+		///    langword="null" /> if it has been processed.
+		/// </summary>
+		/// <remarks>
+		///    Rather than processing the data when the frame is loaded,
+		///    it is parsed on demand, reducing the ammount of
+		///    unnecessary conversion.
+		/// </remarks>
+		private ByteVector raw_data = null;
+		
+		/// <summary>
+		///    Contains the ID3v2 version of <see cref="raw_data" />.
+		/// </summary>
+		private byte raw_version = 0;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="TextInformationFrame" /> with a specified
+		///    identifier and text encoding.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing an ID3v2.4
+		///    frame identifier.
+		/// </param>
+		/// <param name="encoding">
+		///    A <see cref="StringType" /> value specifying the encoding
+		///    to use for the new instance.
+		/// </param>
+		public TextInformationFrame (ByteVector ident,
+		                             StringType encoding)
+			: base (ident, 4)
+		{
+			this.encoding = encoding;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="TextInformationFrame" /> with a specified
+		///    identifer.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing an ID3v2.4
+		///    frame identifier.
+		/// </param>
+		public TextInformationFrame (ByteVector ident)
+			: this (ident, Id3v2.Tag.DefaultEncoding)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="TextInformationFrame" /> by reading its raw
+		///    contents in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the
+		///    frame to read.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the ID3v2 version
+		///    in which <paramref name="data" /> is encoded.
+		/// </param>
+		public TextInformationFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="TextInformationFrame" /> by reading its raw
+		///    contents from a specifed position in a <see
+		///    cref="ByteVector" /> object in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the frame
+		///    to read.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the offset in
+		///    <paramref name="data" /> at which the frame begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> value containing the header
+		///    that would be read in the frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the ID3v2 version
+		///    in which <paramref name="data" /> is encoded.
+		/// </param>
+		protected internal TextInformationFrame (ByteVector data,
+		                                         int offset,
+		                                         FrameHeader header,
+		                                         byte version)
+			: base (header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the text contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="StringCollection" /> object containing the
+		///    text contained in the current instance.
+		/// </value>
+		/// <remarks>
+		///    Modifying the contents of the returned value will not
+		///    modify the contents of the current instance.
+		/// </remarks>
+		[Obsolete("Use TextInformationFrame.Text")]
+		public StringCollection FieldList {
+			get {
+				ParseRawData ();
+				return new StringCollection (Text);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the text contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the text contained
+		///    in the current instance.
+		/// </value>
+		/// <remarks>
+		///    <para>Modifying the contents of the returned value will
+		///    not modify the contents of the current instance. The
+		///    value must be reassigned for the value to change.</para>
+		/// </remarks>
+		/// <example>
+		///    <para>Modifying the values text values of a frame.</para>
+		///    <code>TextInformationFrame frame = TextInformationFrame.Get (myTag, "TPE1", true);
+		////* Upper casing all the text: */
+		///string[] text = frame.Text;
+		///for (int i = 0; i &lt; text.Length; i++)
+		///	text [i] = text [i].ToUpper ();
+		///frame.Text = text;
+		///
+		////* Replacing the value completely: */
+		///frame.Text = new string [] {"DJ Jazzy Jeff"};</code>
+		/// </example>
+		public virtual string [] Text {
+			get {
+				ParseRawData ();
+				return (string[]) text_fields.Clone ();
+			}
+			set {
+				raw_data = null;
+				text_fields = value != null ?
+					(string[]) value.Clone () :
+					new string [0];
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the text encoding to use when rendering
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="StringType" /> value specifying the encoding
+		///    to use when rendering the current instance.
+		/// </value>
+		/// <remarks>
+		///    This value will be overwritten if <see
+		///    cref="TagLib.Id3v2.Tag.ForceDefaultEncoding" /> is <see
+		///    langword="true" />.
+		/// </remarks>
+		public StringType TextEncoding {
+			get {
+				ParseRawData ();
+				return encoding;
+			}
+			set {encoding = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Sets the text contained in the current instance.
+		/// </summary>
+		/// <param name="fields">
+		///    A <see cref="StringCollection" /> object containing text
+		///    to store in the current instance.
+		/// </param>
+		[Obsolete("Use TextInformationFrame.Text")]
+		public void SetText (StringCollection fields)
+		{
+			raw_data = null;
+			Text = fields != null ? fields.ToArray () : null;
+		}
+		
+		/// <summary>
+		///    Sets the text contained in the current instance.
+		/// </summary>
+		/// <param name="text">
+		///    A <see cref="string[]" /> containing text to store in the
+		///    current instance.
+		/// </param>
+		[Obsolete("Use TextInformationFrame.Text")]
+		public void SetText (params string [] text)
+		{
+			raw_data = null;
+			Text = text;
+		}
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> containing the joined text.
+		/// </returns>
+		public override string ToString ()
+		{
+			ParseRawData ();
+			return string.Join ("; ", Text);
+		}
+		
+		/// <summary>
+		///    Renders the current instance, encoded in a specified
+		///    ID3v2 version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> value specifying the version of
+		///    ID3v2 to use when encoding the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public override ByteVector Render (byte version)
+		{
+			if (version != 3 || FrameId != FrameType.TDRC)
+				return base.Render (version);
+			
+			string text = ToString ();
+			if (text.Length < 10 || text [4] != '-' ||
+				text [7] != '-')
+				return base.Render (version);
+			
+			ByteVector output = new ByteVector ();
+			TextInformationFrame f;
+			
+			f = new TextInformationFrame (FrameType.TYER, encoding);
+			f.Text = new string [] {text.Substring (0, 4)};
+			output.Add (f.Render (version));
+			
+			f = new TextInformationFrame (FrameType.TDAT, encoding);
+			f.Text = new string [] {
+				text.Substring (5, 2) + text.Substring (8, 2)
+			};
+			output.Add (f.Render (version));
+			
+			if (text.Length < 16 || text [10] != 'T' ||
+				text [13] != ':')
+				return output;
+			
+			f = new TextInformationFrame (FrameType.TIME, encoding);
+			f.Text = new string [] {
+				text.Substring (11, 2) + text.Substring (14, 2)
+			};
+			output.Add (f.Render (version));
+			
+			return output;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a <see cref="TextInformationFrame" /> object of a
+		///    specified type from a specified tag, optionally creating
+		///    and adding one with a specified encoding if none is
+		///    found.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search for the specified
+		///    tag in.
+		/// </param>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the frame
+		///    identifer to search for.
+		/// </param>
+		/// <param name="encoding">
+		///    A <see cref="StringType" /> value specifying the encoding
+		///    to use if a new frame is created.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    create a new frame if an existing frame was not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TextInformationFrame" /> object containing
+		///    the frame found in or added to <paramref name="tag" /> or
+		///    <see langword="null" /> if no value was found <paramref
+		///    name="create" /> is <see langword="false" />.
+		/// </returns>
+		/// <remarks>
+		///    To create a frame without having to specify the encoding,
+		///    use <see cref="Get(Tag,ByteVector,bool)" />.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="tag" /> or <paramref name="type" /> is
+		///    <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="type" /> is not exactly four bytes long.
+		/// </exception>
+		public static TextInformationFrame Get (Tag tag,
+		                                        ByteVector ident,
+		                                        StringType encoding,
+		                                        bool create)
+		{
+			if (tag == null)
+				throw new ArgumentNullException ("tag");
+			
+			if (ident == null)
+				throw new ArgumentNullException ("ident");
+			
+			if (ident.Count != 4)
+				throw new ArgumentException (
+					"Identifier must be four bytes long.",
+					"ident");
+			
+			foreach (TextInformationFrame frame in
+				tag.GetFrames<TextInformationFrame> (ident))
+				return frame;
+			
+			if (!create)
+				return null;
+			
+			TextInformationFrame new_frame =
+				new TextInformationFrame (ident, encoding);
+			tag.AddFrame (new_frame);
+			return new_frame;
+		}
+		
+		/// <summary>
+		///    Gets a <see cref="TextInformationFrame" /> object of a
+		///    specified type from a specified tag, optionally creating
+		///    and adding one if none is found.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search for the specified
+		///    tag in.
+		/// </param>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the frame
+		///    identifer to search for.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    create a new frame if an existing frame was not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TextInformationFrame" /> object containing
+		///    the frame found in or added to <paramref name="tag" /> or
+		///    <see langword="null" /> if no value was found <paramref
+		///    name="create" /> is <see langword="false" />.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="tag" /> or <paramref name="ident" /> is
+		///    <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="ident" /> is not exactly four bytes long.
+		/// </exception>
+		public static TextInformationFrame Get (Tag tag,
+		                                        ByteVector ident,
+		                                        bool create)
+		{
+			return Get (tag, ident, Tag.DefaultEncoding, create);
+		}
+		
+		/// <summary>
+		///    Gets a <see cref="TextInformationFrame" /> object of a
+		///    specified type from a specified tag.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search for the specified
+		///    tag in.
+		/// </param>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the frame
+		///    identifer to search for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TextInformationFrame" /> object containing
+		///    the frame found in <paramref name="tag" /> or <see
+		///    langword="null" /> if no value was found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="tag" /> or <paramref name="type" /> is
+		///    <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="type" /> is not exactly four bytes long.
+		/// </exception>
+		[Obsolete("Use TextInformationFrame.Get(Tag,ByteVector,bool)")]
+		public static TextInformationFrame Get (Tag tag,
+		                                        ByteVector ident)
+		{
+			return Get (tag, ident, false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			raw_data = data;
+			raw_version = version;
+		}
+		
+		/// <summary>
+		///    Performs the actual parsing of the raw data.
+		/// </summary>
+		/// <remarks>
+		///    Because of the high parsing cost and relatively low usage
+		///    of the class, <see cref="ParseFields" /> only stores the
+		///    field data so it can be parsed on demand. Whenever a
+		///    property or method is called which requires the data,
+		///    this method is called, and only on the first call does it
+		///    actually parse the data.
+		/// </remarks>
+		protected void ParseRawData ()
+		{
+			if (raw_data == null)
+				return;
+			
+			ByteVector data = raw_data;
+			raw_data = null;
+			
+			// read the string data type (the first byte of the
+			// field data)
+			encoding = (StringType) data [0];
+			List<string> field_list = new List<string> ();
+			
+			ByteVector delim = ByteVector.TextDelimiter (encoding);
+			
+			if (raw_version > 3 || FrameId == FrameType.TXXX) {
+				field_list.AddRange (data.ToStrings (encoding, 1));
+			} else if (data.Count > 1 && !data.Mid (1,
+				delim.Count).Equals (delim)) {
+				string value = data.ToString (encoding, 1,
+					data.Count - 1);
+				
+				// Truncate values containing NULL bytes
+				int null_index = value.IndexOf ('\x00');
+				if (null_index >= 0) {
+					value = value.Substring (0, null_index);
+				}
+				
+				if (FrameId == FrameType.TCOM ||
+					FrameId == FrameType.TEXT ||
+					FrameId == FrameType.TOLY ||
+					FrameId == FrameType.TOPE ||
+					FrameId == FrameType.TPE1 ||
+					FrameId == FrameType.TPE2 ||
+					FrameId == FrameType.TPE3 ||
+					FrameId == FrameType.TPE4) {
+					field_list.AddRange (value.Split ('/'));
+				} else if (FrameId == FrameType.TCON) {
+					while (value.Length > 1 && value [0] == '(') {
+						int closing = value.IndexOf (')');
+						if (closing < 0)
+							break;
+						
+						string number = value.Substring (1,
+								closing - 1);
+						
+						field_list.Add (number);
+						
+						value = value.Substring (
+							closing + 1).TrimStart ('/', ' ');
+						
+						string text = Genres.IndexToAudio (number);
+						if (text != null && value.StartsWith (text))
+							value = value.Substring (text.Length)
+								.TrimStart ('/', ' ');
+					}
+					
+					if (value.Length > 0)
+						field_list.AddRange (value.Split (new char [] {'/'}));
+				} else {
+					field_list.Add (value);
+				}
+			}
+			
+			// Bad tags may have one or more nul characters at the
+			// end of a string, resulting in empty strings at the
+			// end of the FieldList. Strip them off.
+			while (field_list.Count != 0 &&
+				string.IsNullOrEmpty (field_list [
+					field_list.Count - 1]))
+				field_list.RemoveAt (field_list.Count - 1);
+			
+			text_fields = field_list.ToArray ();
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version) {
+			if (raw_data != null && raw_version == version)
+				return raw_data;
+			
+			StringType encoding = CorrectEncoding (TextEncoding,
+				version);
+			ByteVector v = new ByteVector ((byte) encoding);
+			string [] text = text_fields;
+			
+			bool txxx = FrameId == FrameType.TXXX;
+			
+			if (version > 3 || txxx) {
+				
+				if (txxx) {
+					if (text.Length == 0)
+						text = new string [] {null, null};
+					else if (text.Length == 1)
+						text = new string [] {text [0],
+							null};
+				}
+				
+				for (int i = 0; i < text.Length; i++) {
+					// 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 (i != 0)
+						v.Add (ByteVector.TextDelimiter (
+							encoding));
+						
+					if (text [i] != null)
+						v.Add (ByteVector.FromString (
+							text [i],
+							encoding));
+				}
+			} else if (FrameId == FrameType.TCON) {
+				byte id;
+				bool prev_value_indexed = true;
+				StringBuilder data = new StringBuilder ();
+				foreach (string s in text) {
+					if (!prev_value_indexed) {
+						data.Append ("/").Append (s);
+						continue;
+					}
+					
+					if (prev_value_indexed =
+						byte.TryParse (s, out id))
+						data.AppendFormat (
+							CultureInfo.InvariantCulture,
+								"({0})", id);
+					else
+						data.Append (s);
+				}
+				
+				v.Add (ByteVector.FromString (data.ToString (),
+					encoding));
+			} else {
+				v.Add (ByteVector.FromString (
+					string.Join ("/", text), encoding));
+			}
+			
+			return v;
+		}
+		
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			TextInformationFrame frame =
+				(this is UserTextInformationFrame) ?
+				new UserTextInformationFrame (null, encoding) :
+				new TextInformationFrame (FrameId, encoding);
+			frame.text_fields = (string[]) text_fields.Clone ();
+			if (raw_data != null)
+				frame.raw_data = new ByteVector (raw_data);
+			frame.raw_version = raw_version;
+			return frame;
+		}
+		
+#endregion
+	}
+	
+	
+	
+	/// <summary>
+	///    This class extends <see cref="TextInformationFrame" /> to provide
+	///    support for ID3v2 User Text Information (TXXX) Frames.
+	/// </summary>
+	public class UserTextInformationFrame : TextInformationFrame
+	{
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UserTextInformationFrame" /> with a specified
+		///    description and text encoding.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <param name="encoding">
+		///    A <see cref="StringType" /> containing the text encoding
+		///    to use when rendering the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see
+		///    cref="Get(Tag,string,StringType,bool)" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public UserTextInformationFrame (string description,
+		                                 StringType encoding)
+			: base (FrameType.TXXX, encoding)
+		{
+			base.Text = new string [] {description};
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UserTextInformationFrame" /> with a specified
+		///    description.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see
+		///    cref="Get(Tag,string,bool)" /> for more integrated frame
+		///    creation.
+		/// </remarks>
+		public UserTextInformationFrame (string description)
+			: base (FrameType.TXXX)
+		{
+			base.Text = new string [] {description};
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UserTextInformationFrame" /> by reading its raw
+		///    data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public UserTextInformationFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UserTextInformationFrame" /> by reading its raw
+		///    data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal UserTextInformationFrame (ByteVector data,
+		                                             int offset,
+		                                             FrameHeader header,
+		                                             byte version)
+			: base (data, offset, header, version)
+		{
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the description stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the description
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description per tag.
+		/// </remarks>
+		public string Description {
+			get {
+				string [] text = base.Text;
+				return text.Length > 0 ? text [0] : null;
+			}
+			
+			set {
+				string [] text = base.Text;
+				if (text.Length > 0)
+					text [0] = value;
+				else
+					text = new string [] {value};
+				
+				base.Text = text;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the text contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the text contained
+		///    in the current instance.
+		/// </value>
+		/// <remarks>
+		///    <para>Modifying the contents of the returned value will
+		///    not modify the contents of the current instance. The
+		///    value must be reassigned for the value to change.</para>
+		/// </remarks>
+		public override string [] Text {
+			get {
+				string [] text = base.Text;
+				if (text.Length < 2)
+					return new string [0];
+				
+				string [] new_text = new string [text.Length - 1];
+				for (int i = 0; i < new_text.Length; i ++)
+					new_text [i] = text [i+1];
+				
+				return new_text;
+			}
+			set {
+				string [] new_value = new string [
+					value != null ? (value.Length + 1) : 1];
+				
+				new_value [0] = Description;
+				
+				for (int i = 1; i < new_value.Length; i ++)
+					new_value [i] = value [i - 1];
+				
+				base.Text = new_value;
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> containing the joined text.
+		/// </returns>
+		public override string ToString ()
+		{
+			return new StringBuilder ().Append ("[")
+				.Append (Description)
+				.Append ("] ")
+				.Append (base.ToString ()).ToString ();
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified user text frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="StringType" /> specifying the encoding to
+		///    use if creating a new frame.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="UserTextInformationFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found and <paramref name="create" /> is
+		///    <see langword="false" />.
+		/// </returns>
+		public static UserTextInformationFrame Get (Tag tag,
+		                                            string description,
+		                                            StringType type,
+		                                            bool create)
+		{
+			if (tag == null)
+				throw new ArgumentNullException ("tag");
+			
+			if (description == null)
+				throw new ArgumentNullException ("description");
+			
+			if (description.Length == 0)
+				throw new ArgumentException (
+					"Description must not be empty.",
+					"description");
+			
+			foreach (UserTextInformationFrame frame in
+				tag.GetFrames<UserTextInformationFrame> (
+					FrameType.TXXX))
+				if (description.Equals (frame.Description))
+					return frame;
+			
+			if (!create)
+				return null;
+			
+			UserTextInformationFrame new_frame =
+				new UserTextInformationFrame (description,
+					type);
+			tag.AddFrame (new_frame);
+			return new_frame;
+		}
+		
+		/// <summary>
+		///    Gets a specified user text frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="UserTextInformationFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found and <paramref name="create" /> is
+		///    <see langword="false" />.
+		/// </returns>
+		public static UserTextInformationFrame Get (Tag tag,
+		                                            string description,
+		                                            bool create)
+		{
+			return Get (tag, description, Tag.DefaultEncoding,
+				create);
+		}
+		
+		/// <summary>
+		///    Gets a specified user text frame from the specified tag.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="UserTextInformationFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found.
+		/// </returns>
+		[Obsolete("Use UserTextInformationFrame.Get(Tag,string,bool)")]
+		public static UserTextInformationFrame Get (Tag tag,
+		                                            string description)
+		{
+			return Get (tag, description, false);
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/UniqueFileIdentifierFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/UniqueFileIdentifierFrame.cs
new file mode 100644
index 0000000..2c0509d
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/UniqueFileIdentifierFrame.cs
@@ -0,0 +1,325 @@
+//
+// UniqueFileIdentifierFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   uniquefileidentifierframe.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2004 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Unique File Identifier (UFID) Frames.
+	/// </summary>
+	public class UniqueFileIdentifierFrame : Frame
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the owner string.
+		/// </summary>
+		private string owner = null;
+		
+		/// <summary>
+		///    Contains the identifier data.
+		/// </summary>
+		private ByteVector identifier = null;
+		
+#endregion
+      
+      
+      
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UniqueFileIdentifierFrame" /> with a specified
+		///    owner and identifier data.
+		/// </summary>
+		/// <param name="owner">
+		///    A <see cref="string" /> containing the owner of the new
+		///    frame.
+		/// </param>
+		/// <param name="identifier">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier for the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see
+		///    cref="Get(Tag,string,bool)" /> for more integrated frame
+		///    creation.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="owner" /> is <see langword="null" />.
+		/// </exception>
+		public UniqueFileIdentifierFrame (string owner,
+		                                  ByteVector identifier)
+			: base (FrameType.UFID, 4)
+		{
+			if (owner == null)
+				throw new ArgumentNullException ("owner");
+			
+			this.owner = owner;
+			this.identifier = identifier;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UniqueFileIdentifierFrame" /> with a specified
+		///    owner.
+		/// </summary>
+		/// <param name="owner">
+		///    A <see cref="string" /> containing the owner of the new
+		///    frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see
+		///    cref="Get(Tag,string,bool)" /> for more integrated frame
+		///    creation.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="owner" /> is <see langword="null" />.
+		/// </exception>
+		public UniqueFileIdentifierFrame (string owner)
+			: this (owner, null)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UniqueFileIdentifierFrame" /> by reading its raw
+		///    data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public UniqueFileIdentifierFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UniqueFileIdentifierFrame" /> by reading its raw
+		///    data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal UniqueFileIdentifierFrame (ByteVector data,
+		                                              int offset,
+		                                              FrameHeader header,
+		                                              byte version)
+			: base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the owner of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the owner of the
+		///    current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching owner per
+		///    tag.
+		/// </remarks>
+		public string Owner {
+			get {return owner;}
+		}
+
+		/// <summary>
+		///    Gets and sets the identifier data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containiner the unique
+		///    file identifier frame.
+		/// </value>
+		public ByteVector Identifier {
+			get {return identifier;}
+			set {identifier = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified unique file identifer frame from the
+		///    specified tag, optionally creating it if it does not
+		///    exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="owner">
+		///    A <see cref="string" /> specifying the owner to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="UserTextInformationFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found and <paramref name="create" /> is
+		///    <see langword="false" />.
+		/// </returns>
+		public static UniqueFileIdentifierFrame Get (Tag tag,
+		                                             string owner,
+		                                             bool create)
+		{
+			UniqueFileIdentifierFrame ufid;
+			
+			foreach (Frame frame in tag.GetFrames (FrameType.UFID)) {
+				ufid = frame as UniqueFileIdentifierFrame;
+				
+				if (ufid == null)
+					continue;
+				
+				if (ufid.Owner == owner)
+					return ufid;
+			}
+			
+			if (!create)
+				return null;
+			
+			ufid = new UniqueFileIdentifierFrame (owner, null);
+			tag.AddFrame (ufid);
+			return ufid;
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			ByteVectorCollection fields =
+				ByteVectorCollection.Split (data, (byte) 0);
+			
+			if (fields.Count != 2)
+				return;
+			
+			owner = fields [0].ToString (StringType.Latin1);
+			identifier = fields [1];
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			ByteVector data = new ByteVector ();
+			
+			data.Add (ByteVector.FromString (owner, StringType.Latin1));
+			data.Add (ByteVector.TextDelimiter (StringType.Latin1));
+			data.Add (identifier);
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			UniqueFileIdentifierFrame frame =
+				new UniqueFileIdentifierFrame (owner);
+			if (identifier != null)
+				frame.identifier = new ByteVector (identifier);
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/UnknownFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/UnknownFrame.cs
new file mode 100644
index 0000000..19ba215
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/UnknownFrame.cs
@@ -0,0 +1,208 @@
+//
+// UnknownFrame.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   unknownframe.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002 Scott Wheeler (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    This class extends <see cref="Frame" /> to provide a fallback
+	///    type when no other frame class works for a given frame.
+	/// </summary>
+	public class UnknownFrame : Frame
+	{
+#region Private Properties
+		
+		/// <summary>
+		///    Contains the field data.
+		/// </summary>
+		private ByteVector field_data = null;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnknownFrame" /> with a specified type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing an ID3v2.4
+		///    frame identifier.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> object containing the contents
+		///    of the frame.
+		/// </param>
+		public UnknownFrame (ByteVector type, ByteVector data)
+			: base (type, 4)
+		{
+			field_data = data;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnknownFrame" /> with a specified type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing an ID3v2.4
+		///    frame identifier.
+		/// </param>
+		public UnknownFrame (ByteVector type) : this (type, null)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnknownFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public UnknownFrame (ByteVector data, byte version)
+			: base (data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnknownFrame" /> by reading its raw data in a
+		///    specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal UnknownFrame (ByteVector data, int offset,
+		                                 FrameHeader header,
+		                                 byte version)
+			: base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the field data in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> containing the field data.
+		/// </value>
+		public ByteVector Data {
+			get {return field_data;}
+			set {field_data = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> object describing the current
+		///    instance.
+		/// </returns>
+		public override string ToString ()
+		{
+			return base.ToString ();
+		}
+		
+#endregion
+				
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			field_data = data;
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields (byte version)
+		{
+			return field_data ?? new ByteVector ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Frames/UnsynchronisedLyricsFrame.cs b/lib/TagLib/TagLib/Id3v2/Frames/UnsynchronisedLyricsFrame.cs
new file mode 100644
index 0000000..56e99af
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Frames/UnsynchronisedLyricsFrame.cs
@@ -0,0 +1,530 @@
+//
+// UnsynchronisedLyricsFrame.cs:
+//
+// Author:
+//   Patrick Laplante
+//
+// Original Source:
+//   TagLib.Id3v2.CommentsFrame
+//
+// Copyright (C) 2007 Brian Nickel (Original Implementation)
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using TagLib.Id3v2;
+
+namespace TagLib.Id3v2
+{
+	/// <summary>
+	///    This class extends <see cref="Frame" />, implementing support for
+	///    ID3v2 Unsynchronised Lyrics (USLT) Frames.
+	/// </summary>
+	public class UnsynchronisedLyricsFrame : Frame
+	{
+#region Private Properties
+		
+		/// <summary>
+		///    Contains the text encoding to use when rendering the
+		///    current instance.
+		/// </summary>
+		private StringType encoding = Tag.DefaultEncoding;
+		
+		/// <summary>
+		///    Contains the ISO-639-2 language code of the current
+		///    instance.
+		/// </summary>
+		private string language = null;
+		
+		/// <summary>
+		///    Contains the description of the current instance.
+		/// </summary>
+		private string description = null;
+		
+		/// <summary>
+		///    Contains the lyrics text of the current instance.
+		/// </summary>
+		private string text = null;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsynchronisedLyricsFrame" /> with a specified
+		///    description, ISO-639-2 language code, and text encoding.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code of the new frame.
+		/// </param>
+		/// <param name="encoding">
+		///    A <see cref="StringType" /> containing the text encoding
+		///    to use when rendering the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public UnsynchronisedLyricsFrame (string description,
+		                                  string language,
+		                                  StringType encoding)
+			: base (FrameType.USLT, 4)
+		{
+			this.encoding    = encoding;
+			this.language    = language;
+			this.description = description;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsynchronisedLyricsFrame" /> with a specified
+		///    description and ISO-639-2 language code.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code of the new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public UnsynchronisedLyricsFrame (string description,
+		                                  string language)
+			: this (description, language,
+				TagLib.Id3v2.Tag.DefaultEncoding)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsynchronisedLyricsFrame" /> with a specified
+		///    description.
+		/// </summary>
+		/// <param name="description">
+		///    A <see cref="string" /> containing the description of the
+		///    new frame.
+		/// </param>
+		/// <remarks>
+		///    When a frame is created, it is not automatically added to
+		///    the tag. Consider using <see cref="Get" /> for more
+		///    integrated frame creation.
+		/// </remarks>
+		public UnsynchronisedLyricsFrame (string description)
+			: this (description, null)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsynchronisedLyricsFrame" /> by reading its raw
+		///    data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object starting with the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		public UnsynchronisedLyricsFrame (ByteVector data, byte version)
+			: base(data, version)
+		{
+			SetData (data, 0, version, true);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsynchronisedLyricsFrame" /> by reading its raw
+		///    data in a specified ID3v2 version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    representation of the new frame.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> indicating at what offset in
+		///    <paramref name="data" /> the frame actually begins.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="FrameHeader" /> containing the header of the
+		///    frame found at <paramref name="offset" /> in the data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    raw frame is encoded in.
+		/// </param>
+		protected internal UnsynchronisedLyricsFrame (ByteVector data,
+		                                              int offset,
+		                                              FrameHeader header,
+		                                              byte version)
+			: base(header)
+		{
+			SetData (data, offset, version, false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the text encoding to use when storing the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the text encoding to
+		///    use when storing the current instance.
+		/// </value>
+		/// <remarks>
+		///    This encoding is overridden when rendering if <see
+		///    cref="Tag.ForceDefaultEncoding" /> is <see
+		///    langword="true" /> or the render version does not support
+		///    it.
+		/// </remarks>
+		public StringType TextEncoding {
+			get {return encoding;}
+			set {encoding = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the ISO-639-2 language code stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the ISO-639-2 language
+		///    code stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one file with a matching description
+		///    and ISO-639-2 language code per tag.
+		/// </remarks>
+		public string Language {
+			get {
+				if (language != null && language.Length > 2)
+					return language.Substring (0, 3);
+				
+				return "XXX";
+			}
+			set {language = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the description stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the description
+		///    stored in the current instance.
+		/// </value>
+		/// <remarks>
+		///    There should only be one frame with a matching
+		///    description and ISO-639-2 language code per tag.
+		/// </remarks>
+		public string Description {
+			get {
+				if (description != null)
+					return description;
+				
+				return string.Empty;
+			}
+			set {description = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrical text stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the lyrical text
+		///    stored in the current instance.
+		/// </value>
+		public string Text {
+			get {
+				if (text != null)
+					return text;
+				
+				return string.Empty;
+			}
+			set {text = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Gets a string representation of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> containing the lyrical text.
+		/// </returns>
+		public override string ToString ()
+		{
+			return Text;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets a specified lyrics frame from the specified tag,
+		///    optionally creating it if it does not exist.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> specifying the ISO-639-2 language
+		///   code to match.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> specifying whether or not to create
+		///    and add a new frame to the tag if a match is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="UnsynchronisedLyricsFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found and <paramref name="create" /> is
+		///    <see langword="false" />.
+		/// </returns>
+		public static UnsynchronisedLyricsFrame Get (Tag tag,
+		                                             string description,
+		                                             string language,
+		                                             bool create)
+		{
+			UnsynchronisedLyricsFrame uslt;
+			foreach (Frame frame in tag.GetFrames (FrameType.USLT)) {
+				uslt = frame as UnsynchronisedLyricsFrame;
+				
+				if (uslt == null)
+					continue;
+				
+				if (uslt.Description != description)
+					continue;
+				
+				if (language != null && language != uslt.Language)
+					continue;
+				
+				return uslt;
+			}
+			
+			if (!create)
+				return null;
+			
+			uslt = new UnsynchronisedLyricsFrame (description,
+				language);
+			tag.AddFrame (uslt);
+			return uslt;
+		}
+		
+		/// <summary>
+		///    Gets a specified comments frame from the specified tag,
+		///    trying to to match the description and language but
+		///    accepting an incomplete match.
+		/// </summary>
+		/// <param name="tag">
+		///    A <see cref="Tag" /> object to search in.
+		/// </param>
+		/// <param name="description">
+		///    A <see cref="string" /> specifying the description to
+		///    match.
+		/// </param>
+		/// <param name="language">
+		///    A <see cref="string" /> specifying the ISO-639-2 language
+		///   code to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="UnsynchronisedLyricsFrame" /> object
+		///    containing the matching frame, or <see langword="null" />
+		///    if a match wasn't found.
+		/// </returns>
+		/// <remarks>
+		///    <para>The method tries matching with the following order
+		///    of precidence:</para>
+		///    <list type="number">
+		///       <item><term>The first frame with a matching
+		///       description and language.</term></item>
+		///       <item><term>The first frame with a matching
+		///       language.</term></item>
+		///       <item><term>The first frame with a matching
+		///       description.</term></item>
+		///       <item><term>The first frame.</term></item>
+		///    </list>
+		/// </remarks>
+		public static UnsynchronisedLyricsFrame GetPreferred (Tag tag,
+		                                                      string description,
+		                                                      string language)
+		{
+			// This is weird, so bear with me. The best thing we can
+			// have is something straightforward and in our own
+			// language. If it has a description, then it is
+			// probably used for something other than an actual
+			// comment. If that doesn't work, we'd still rather have
+			// something in our language than something in another.
+			// After that all we have left are things in other
+			// languages, so we'd rather have one with actual
+			// content, so we try to get one with no description
+			// first.
+			
+			int best_value = -1;
+			UnsynchronisedLyricsFrame best_frame = null;
+			
+			foreach (Frame frame in tag.GetFrames (FrameType.USLT)) {
+				UnsynchronisedLyricsFrame uslt =
+					frame as UnsynchronisedLyricsFrame;
+				
+				if (uslt == null)
+					continue;
+				
+				bool same_name = uslt.Description == description;
+				bool same_lang = uslt.Language == language;
+				
+				if (same_name && same_lang)
+					return uslt;
+				
+				int value = same_lang ? 2 : same_name ? 1 : 0;
+				
+				if (value <= best_value)
+					continue;
+				
+				best_value = value;
+				best_frame = uslt;
+			}
+			
+			return best_frame;
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the values in the current instance by parsing
+		///    its field data in a specified version.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    extracted field data.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is encoded in.
+		/// </param>
+		protected override void ParseFields (ByteVector data,
+		                                     byte version)
+		{
+			if (data.Count < 4)
+				throw new CorruptFileException (
+					"Not enough bytes in field.");
+			
+			encoding = (StringType) data [0];
+			language = data.ToString (StringType.Latin1, 1, 3);
+			
+			string [] split = data.ToStrings (encoding, 4, 2);
+			
+			if (split.Length == 1) {
+				// Bad lyrics frame. Assume that it lacks a
+				// description.
+				description = String.Empty;
+				text = split [0];
+			} else {
+				description = split [0];
+				text = split [1];
+			}
+		}
+		
+		/// <summary>
+		///    Renders the values in the current instance into field
+		///    data for a specified version.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="byte" /> indicating the ID3v2 version the
+		///    field data is to be encoded in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered field data.
+		/// </returns>
+		protected override ByteVector RenderFields(byte version)
+		{
+			StringType encoding = CorrectEncoding (TextEncoding,
+				version);
+			ByteVector v = new ByteVector();
+			
+			v.Add ((byte) encoding);
+			v.Add (ByteVector.FromString (Language, StringType.Latin1));
+			v.Add (ByteVector.FromString (description, encoding));
+			v.Add (ByteVector.TextDelimiter(encoding));
+			v.Add (ByteVector.FromString (text, encoding));
+
+		return v;
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Frame" /> object identical to the
+		///    current instance.
+		/// </returns>
+		public override Frame Clone ()
+		{
+			UnsynchronisedLyricsFrame frame =
+				new UnsynchronisedLyricsFrame (description,
+					language, encoding);
+			frame.text = text;
+			return frame;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Header.cs b/lib/TagLib/TagLib/Id3v2/Header.cs
new file mode 100644
index 0000000..da4da0f
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Header.cs
@@ -0,0 +1,320 @@
+//
+// Header.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v2header.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    Indicates the flags applied to a <see cref="Header" /> object.
+	/// </summary>
+	[Flags]
+	public enum HeaderFlags : byte {
+		/// <summary>
+		///    The header contains no flags.
+		/// </summary>
+		None = 0,
+		
+		/// <summary>
+		///    The tag described by the header has been unsynchronized.
+		/// </summary>
+		Unsynchronisation = 0x80,
+		
+		/// <summary>
+		///    The tag described by the header has contains an extended
+		///    header.
+		/// </summary>
+		ExtendedHeader = 0x40,
+		
+		/// <summary>
+		///    The tag described by the header is experimental.
+		/// </summary>
+		ExperimentalIndicator = 0x20,
+		
+		/// <summary>
+		///    The tag described by the header contains a footer.
+		/// </summary>
+		FooterPresent = 0x10
+	}
+	
+	/// <summary>
+	///    This structure provides a representation of an ID3v2 tag header
+	///    which can be read from and written to disk.
+	/// </summary>
+	public struct Header
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the tag's major version.
+		/// </summary>
+		private byte major_version;
+		
+		/// <summary>
+		///    Contains the tag's version revision.
+		/// </summary>
+		private byte revision_number;
+		
+		/// <summary>
+		///    Contains tag's flags.
+		/// </summary>
+		private HeaderFlags flags;
+		
+		/// <summary>
+		///    Contains the tag size.
+		/// </summary>
+		private uint tag_size;
+		
+#endregion
+		
+		
+		
+#region Public Fields
+		
+		/// <summary>
+		///    The size of a ID3v2 header.
+		/// </summary>
+		public const uint Size = 10;
+		
+		/// <summary>
+		///    The identifier used to recognize a ID3v2 headers.
+		/// </summary>
+		/// <value>
+		///    "ID3"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "ID3";
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Header" /> by reading it from raw header data.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data to build the new instance from.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> is smaller than <see
+		///    cref="Size" />, does not begin with <see
+		///    cref="FileIdentifier" />, contains invalid flag data,
+		///    or contains invalid size data.
+		/// </exception>
+		public Header (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"Provided data is smaller than object size.");
+			
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"Provided data does not start with the file identifier");
+			
+			major_version = data [3];
+			revision_number = data [4];
+			flags = (HeaderFlags) data [5];
+			
+			if (major_version == 2 && ((int) flags & 127) != 0)
+				throw new CorruptFileException (
+					"Invalid flags set on version 2 tag.");
+			
+			if (major_version == 3 && ((int) flags & 15) != 0)
+				throw new CorruptFileException (
+					"Invalid flags set on version 3 tag.");
+			
+			if (major_version == 4 && ((int) flags & 7) != 0)
+				throw new CorruptFileException (
+					"Invalid flags set on version 4 tag.");
+			
+			for (int i = 6; i < 10; i ++)
+				if (data [i] >= 128)
+					throw new CorruptFileException (
+						"One of the bytes in the header was greater than the allowed 128.");
+			
+			tag_size = SynchData.ToUInt (data.Mid (6, 4));
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the major version of the tag described by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value specifying the ID3v2 version
+		///    of tag described by the current instance.
+		/// </value>
+		/// <remarks>
+		///    When the version is set, unsupported header flags will
+		///    automatically be removed from the tag.
+		/// </remarks>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="value" /> is less than 2 or more than 4.
+		/// </exception>
+		public byte MajorVersion {
+			get {
+				return major_version == 0 ? Tag.DefaultVersion :
+					major_version;
+			}
+			set {
+				if (value < 2 || value > 4)
+					throw new ArgumentException (
+						"Version unsupported");
+				
+				if (value < 3)
+					flags &= ~(HeaderFlags.ExtendedHeader |
+						HeaderFlags.ExperimentalIndicator);
+				
+				if (value < 4)
+					flags &= ~HeaderFlags.FooterPresent;
+				
+				major_version = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the version revision number of the tag
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value containing the version
+		///    revision number of the tag represented by the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    This value should always be zeroed. A non-zero value
+		///    indicates an experimental or new version of the format
+		///    which may not be completely understood by the current
+		///    implementation. Some software may refuse to read tags
+		///    with a non-zero value.
+		/// </remarks>
+		public byte RevisionNumber {
+			get {return revision_number;}
+			set {revision_number = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the flags applied to the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="HeaderFlags" /> value
+		///    containing the flags applied to the current instance.
+		/// </value>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="value" /> contains a flag not supported
+		///    by the the ID3v2 version of the current instance.
+		/// </exception>
+		public HeaderFlags Flags {
+			get {return flags;}
+			set {
+				if (0 != (value & (HeaderFlags.ExtendedHeader |
+					HeaderFlags.ExperimentalIndicator)) &&
+					MajorVersion < 3)
+					throw new ArgumentException (
+						"Feature only supported in version 2.3+",
+						"value");
+				
+				if (0 != (value & HeaderFlags.FooterPresent) &&
+					MajorVersion < 3)
+					throw new ArgumentException (
+						"Feature only supported in version 2.4+",
+						"value");
+				
+				flags = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the size of the tag described by the
+		///    current instance, minus the header and footer.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    tag described by the current instance.
+		/// </value>
+		public uint TagSize {
+			get {return tag_size;}
+			set {tag_size = value;}
+		}
+		
+		/// <summary>
+		///    Gets the complete size of the tag described by the
+		///    current instance, including the header and footer.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the complete size
+		///    of the tag described by the current instance.
+		/// </value>
+		public uint CompleteTagSize {
+			get {
+				if ((flags & HeaderFlags.FooterPresent) != 0)
+					return TagSize + Size + Footer.Size;
+				else
+					return TagSize + Size;
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw ID3v2 header.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered header.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector v = new ByteVector ();
+			v.Add (FileIdentifier);
+			v.Add (MajorVersion);
+			v.Add (RevisionNumber);
+			v.Add ((byte)flags);
+			v.Add (SynchData.FromUInt (TagSize));
+			return v;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/SynchData.cs b/lib/TagLib/TagLib/Id3v2/SynchData.cs
new file mode 100644
index 0000000..e3124d7
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/SynchData.cs
@@ -0,0 +1,144 @@
+//
+// SynchData.cs: Provides support for encoding and decoding unsynchronized data
+// and numbers.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   id3v2synchdata.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This static class provides support for encoding and decoding
+	///    unsynchronized data and numbers.
+	/// </summary>
+	/// <remarks>
+	///    Unsynchronization is designed so that portions of the tag won't
+	///    be misinterpreted as MPEG audio stream headers by removing the
+	///    possibility of the synch bytes occuring in the tag.
+	/// </remarks>
+	public static class SynchData
+	{
+		/// <summary>
+		///    Decodes synchronized integer data into a <see
+		///    cref="uint" /> value.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the number
+		///    to decode. Only the first 4 bytes of this value will be
+		///    used.
+		/// </param>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the decoded
+		///    number.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public static uint ToUInt (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			uint sum = 0;
+			int last = data.Count > 4 ? 3 : data.Count - 1;
+			
+			for(int i = 0; i <= last; i++)
+				sum |= (uint) (data [i] & 0x7f)
+					<< ((last - i) * 7);
+			
+			return sum;
+		}
+		
+		/// <summary>
+		///    Encodes a <see cref="uint" /> value as synchronized
+		///    integer data.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="uint" /> value containing the number to
+		///    encode.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the encoded
+		///    number.
+		/// </returns>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="value" /> is greater than 268435455.
+		/// </exception>
+		public static ByteVector FromUInt (uint value)
+		{
+			if ((value >> 28) != 0)
+				throw new ArgumentOutOfRangeException ("value",
+					"value must be less than 268435456.");
+			
+			ByteVector v = new ByteVector (4, 0);
+			
+			for (int i = 0; i < 4; i++)
+				v [i] = (byte) (value >> ((3 - i) * 7) & 0x7f);
+			
+			return v;
+		}
+		
+		/// <summary>
+		///    Unsynchronizes a <see cref="ByteVector" /> object by
+		///    inserting empty bytes where necessary.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object to unsynchronize.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public static void UnsynchByteVector (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			for (int i = data.Count - 2; i >= 0; i --)
+				if (data [i] == 0xFF && (data [i+1] == 0 ||
+					(data [i+1] & 0xE0) != 0))
+					data.Insert (i+1, 0);
+		}
+		
+		/// <summary>
+		///    Resynchronizes a <see cref="ByteVector" /> object by
+		///    removing the added bytes.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object to resynchronize.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public static void ResynchByteVector (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			for (int i = data.Count - 2; i >= 0; i --)
+				if (data [i] == 0xFF && data [i+1] == 0)
+					data.RemoveAt (i+1);
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Id3v2/Tag.cs b/lib/TagLib/TagLib/Id3v2/Tag.cs
new file mode 100644
index 0000000..bd26e97
--- /dev/null
+++ b/lib/TagLib/TagLib/Id3v2/Tag.cs
@@ -0,0 +1,2069 @@
+//
+// Tag.cs: Provide support for reading and writing ID3v2 tags.
+//
+// Authors:
+//   Brian Nickel (brian nickel gmail com)
+//   Gabriel BUrt (gabriel burt gmail com)
+//
+// Original Source:
+//   id3v2tag.cpp from TagLib
+//
+// Copyright (C) 2010 Novell, Inc.
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace TagLib.Id3v2 {
+	/// <summary>
+	///    This class extends <see cref="TagLib.Tag" /> and implements <see
+	///    cref="T:System.Collections.Generic.IEnumerable`1" /> to provide support for reading and
+	///    writing ID3v2 tags.
+	/// </summary>
+	public class Tag : TagLib.Tag, IEnumerable<Frame>, ICloneable
+	{
+#region Private Static Fields
+		
+		/// <summary>
+		///    Contains the language to use for language specific
+		///    fields.
+		/// </summary>
+		private static string language = 
+			CultureInfo.CurrentCulture.ThreeLetterISOLanguageName;
+		
+		/// <summary>
+		///    Contains the field to use for new tags.
+		/// </summary>
+		private static byte default_version = 3;
+		
+		/// <summary>
+		///    Indicates whether or not all tags should be saved in
+		///    <see cref="default_version" />.
+		/// </summary>
+		private static bool force_default_version = false;
+		
+		/// <summary>
+		///    Specifies the default string type to use for new frames.
+		/// </summary>
+		private static StringType default_string_type = StringType.UTF8;
+		
+		/// <summary>
+		///    Specifies whether or not all frames shoudl be saved in
+		///    <see cref="default_string_type" />.
+		/// </summary>
+		private static bool force_default_string_type = false;
+		
+		/// <summary>
+		///    Specifies whether or not numeric genres should be used
+		///    when available.
+		/// </summary>
+		private static bool use_numeric_genres = true;
+		
+#endregion
+		
+		
+		
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the tag's header.
+		/// </summary>
+		private Header header = new Header ();
+		
+		/// <summary>
+		///    Contains the tag's extended header.
+		/// </summary>
+		private ExtendedHeader extended_header = null;
+		
+		/// <summary>
+		///    Contains the tag's frames.
+		/// </summary>
+		private List<Frame> frame_list = new List<Frame> ();
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> with no contents.
+		/// </summary>
+		public Tag ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> by reading the contents from a specified
+		///    position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file from
+		///    which the contents of the new instance is to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the tag.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		public Tag (File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Mode = TagLib.File.AccessMode.Read;
+			
+			if (position < 0 ||
+				position > file.Length - Header.Size)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			Read (file, position);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> by reading the contents from a specified
+		///    <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object to read the tag from.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> does not contain enough data.
+		/// </exception>
+		public Tag (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < Header.Size)
+				throw new CorruptFileException (
+					"Does not contain enough header data.");
+			
+			header = new Header (data);
+			
+			// If the tag size is 0, then this is an invalid tag.
+			// Tags must contain at least one frame.
+			
+			if(header.TagSize == 0)
+				return;
+			
+			if (data.Count - Header.Size < header.TagSize)
+				throw new CorruptFileException (
+					"Does not contain enough tag data.");
+			
+			Parse (data.Mid ((int) Header.Size,
+				(int) header.TagSize));
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Gets all frames contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the frames.
+		/// </returns>
+		public IEnumerable<Frame> GetFrames ()
+		{
+			return frame_list;
+		}
+		
+		/// <summary>
+		///    Gets all frames with a specified identifier contained in
+		///    the current instance.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier of the frames to return.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the frames.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="ident" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="ident" /> is not exactly four bytes long.
+		/// </exception>
+		public IEnumerable<Frame> GetFrames (ByteVector ident)
+		{
+			if (ident == null)
+				throw new ArgumentNullException ("ident");
+			
+			if (ident.Count != 4)
+				throw new ArgumentException (
+					"Identifier must be four bytes long.",
+					"ident");
+			
+			foreach (Frame f in frame_list)
+				if (f.FrameId.Equals (ident))
+					yield return f;
+		}
+		
+		/// <summary>
+		///    Gets all frames with of a specified type contained in
+		///    the current instance.
+		/// </summary>
+		/// <typeparam name="T">
+		///    The type of object, derived from <see cref="Frame" />,
+		///    to return from in the current instance.
+		/// </typeparam>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the frames.
+		/// </returns>
+		public IEnumerable<T> GetFrames <T> () where T : Frame
+		{
+			foreach (Frame f in frame_list) {
+				T tf = f as T;
+				if (tf != null)
+					yield return tf;
+			}
+		}
+		
+		/// <summary>
+		///    Gets all frames with a of type <typeparamref name="T" />
+		///    with a specified identifier contained in the current
+		///    instance.
+		/// </summary>
+		/// <typeparam name="T">
+		///    The type of object, derived from <see cref="Frame" />,
+		///    to return from in the current instance.
+		/// </typeparam>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier of the frames to return.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating
+		///    through the frames.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="ident" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="ident" /> is not exactly four bytes long.
+		/// </exception>
+		public IEnumerable<T> GetFrames <T> (ByteVector ident)
+			where T : Frame
+		{
+			if (ident == null)
+				throw new ArgumentNullException ("ident");
+			
+			if (ident.Count != 4)
+				throw new ArgumentException (
+					"Identifier must be four bytes long.",
+					"ident");
+			
+			foreach (Frame f in frame_list) {
+				T tf = f as T;
+				if (tf != null && f.FrameId.Equals (ident))
+					yield return tf;
+			}
+		}
+		
+		/// <summary>
+		///    Adds a frame to the current instance.
+		/// </summary>
+		/// <param name="frame">
+		///    A <see cref="Frame" /> object to add to the current
+		///    instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="frame" /> is <see langword="null" />.
+		/// </exception>
+		public void AddFrame (Frame frame)
+		{
+			if (frame == null)
+				throw new ArgumentNullException ("frame");
+			
+			frame_list.Add (frame);
+		}
+		
+		/// <summary>
+		///    Replaces an existing frame with a new one in the list
+		///    contained in the current instance, or adds a new one if
+		///    the existing one is not contained.
+		/// </summary>
+		/// <param name="oldFrame">
+		///    A <see cref="Frame" /> object to be replaced.
+		/// </param>
+		/// <param name="newFrame">
+		///    A <see cref="Frame" /> object to add to the current
+		///    instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="oldFrame" /> or <paramref name="newFrame"
+		///    /> is <see langword="null" />.
+		/// </exception>
+		public void ReplaceFrame (Frame oldFrame, Frame newFrame)
+		{
+			if (oldFrame == null)
+				throw new ArgumentNullException ("oldFrame");
+			
+			if (newFrame == null)
+				throw new ArgumentNullException ("newFrame");
+			
+			if (oldFrame == newFrame)
+				return;
+			
+			int i = frame_list.IndexOf (oldFrame);
+			if (i >= 0)
+				frame_list [i] = newFrame;
+			else
+				frame_list.Add (newFrame);
+		}
+		
+		/// <summary>
+		///    Removes a specified frame from the current instance.
+		/// </summary>
+		/// <param name="frame">
+		///    A <see cref="Frame" /> object to remove from the current
+		///    instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="frame" /> is <see langword="null" />.
+		/// </exception>
+		public void RemoveFrame (Frame frame)
+		{
+			if (frame == null)
+				throw new ArgumentNullException ("frame");
+			
+			if (frame_list.Contains (frame))
+				frame_list.Remove (frame);
+		}
+		
+		/// <summary>
+		///    Removes all frames with a specified identifier from the
+		///    current instance.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier of the frames to remove.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="ident" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="ident" /> is not exactly four bytes long.
+		/// </exception>
+		public void RemoveFrames (ByteVector ident)
+		{
+			if (ident == null)
+				throw new ArgumentNullException ("ident");
+			
+			if (ident.Count != 4)
+				throw new ArgumentException (
+					"Identifier must be four bytes long.",
+					"ident");
+			
+			for (int i = frame_list.Count - 1; i >= 0; i --)
+				if (frame_list [i].FrameId.Equals (ident))
+					frame_list.RemoveAt (i);
+		}
+		
+		/// <summary>
+		///    Sets the text for a specified Text Information Frame.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier of the frame to set the data for.
+		/// </param>
+		/// <param name="text">
+		///    A <see cref="string[]" /> containing the text to set for
+		///    the specified frame, or <see langword="null" /> to unset
+		///    the value.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="ident" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="ident" /> is not exactly four bytes long.
+		/// </exception>
+		public void SetTextFrame (ByteVector ident,
+		                          params string [] text)
+		{
+			if (ident == null)
+				throw new ArgumentNullException ("ident");
+			
+			if (ident.Count != 4)
+				throw new ArgumentException (
+					"Identifier must be four bytes long.",
+					"ident");
+			
+			bool empty = true;
+			
+			if (text != null)
+				for (int i = 0; empty && i < text.Length; i ++)
+					if (!string.IsNullOrEmpty (text [i]))
+						empty = false;
+			
+			if (empty) {
+				RemoveFrames (ident);
+				return;
+			}
+			
+			TextInformationFrame frame =
+				TextInformationFrame.Get (this, ident, true);
+			
+			frame.Text = text;
+			frame.TextEncoding = DefaultEncoding;
+		}
+		
+		/// <summary>
+		///    Sets the text for a specified Text Information Frame.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier of the frame to set the data for.
+		/// </param>
+		/// <param name="text">
+		///    A <see cref="StringCollection" /> object containing the
+		///    text to set for the specified frame, or <see
+		///    langword="null" /> to unset the value.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="ident" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="ident" /> is not exactly four bytes long.
+		/// </exception>
+		[Obsolete("Use SetTextFrame(ByteVector,String[])")]
+		public void SetTextFrame (ByteVector ident,
+		                          StringCollection text)
+		{
+			if (text == null || text.Count == 0)
+				RemoveFrames (ident);
+			else
+				SetTextFrame (ident, text.ToArray ());
+		}
+		
+		/// <summary>
+		///    Sets the numeric values for a specified Text Information
+		///    Frame.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the
+		///    identifier of the frame to set the data for.
+		/// </param>
+		/// <param name="number">
+		///    A <see cref="uint" /> value containing the number to
+		///    store.
+		/// </param>
+		/// <param name="count">
+		///    A <see cref="uint" /> value representing a total which
+		///    <paramref name="number" /> is a part of, or zero if
+		///    <paramref name="number" /> is not part of a set.
+		/// </param>
+		/// <remarks>
+		///    If both <paramref name="number" /> and <paramref
+		///    name="count" /> are equal to zero, the value will be
+		///    cleared. If <paramref name="count" /> is zero, <paramref
+		///    name="number" /> by itself will be stored. Otherwise, the
+		///    values will be stored as "<paramref name="number"
+		///    />/<paramref name="count" />".
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="ident" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="ident" /> is not exactly four bytes long.
+		/// </exception>
+		public void SetNumberFrame (ByteVector ident, uint number,
+		                            uint count)
+		{
+			if (ident == null)
+				throw new ArgumentNullException ("ident");
+			
+			if (ident.Count != 4)
+				throw new ArgumentException (
+					"Identifier must be four bytes long.",
+					"ident");
+			
+			if (number == 0 && count == 0) {
+				RemoveFrames (ident);
+			} else if (count != 0) {
+				SetTextFrame (ident, string.Format (
+					CultureInfo.InvariantCulture, "{0}/{1}",
+					number, count));
+			} else {
+				SetTextFrame (ident, number.ToString (
+					CultureInfo.InvariantCulture));
+			}
+		}
+		
+		/// <summary>
+		///    Renders the current instance as a raw ID3v2 tag.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered tag.
+		/// </returns>
+		/// <remarks>
+		///    By default, tags will be rendered in the version they
+		///    were loaded in, and new tags using the version specified
+		///    by <see cref="DefaultVersion" />. If <see
+		///    cref="ForceDefaultVersion" /> is <see langword="true" />,
+		///    all tags will be rendered in using the version specified
+		///    by <see cref="DefaultVersion" />, except for tags with
+		///    footers, which must be in version 4.
+		/// </remarks>
+		public ByteVector Render ()
+		{
+			// 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
+			// Header.TagSize) includes the extended header, frames
+			// and padding, but does not include the tag's header or
+			// footer.
+			
+			bool has_footer = (header.Flags &
+				HeaderFlags.FooterPresent) != 0;
+			bool unsynchAtFrameLevel = (header.Flags & HeaderFlags.Unsynchronisation) != 0 && Version >= 4;
+			bool unsynchAtTagLevel = (header.Flags & HeaderFlags.Unsynchronisation) != 0 && Version < 4;
+
+			header.MajorVersion = has_footer ? (byte) 4 : Version;
+			
+			ByteVector tag_data = new ByteVector ();
+			
+			// TODO: Render the extended header.
+			header.Flags &= ~HeaderFlags.ExtendedHeader;
+			
+			// Loop through the frames rendering them and adding
+			// them to the tag_data.
+			foreach (Frame frame in frame_list) {
+				if (unsynchAtFrameLevel)
+					frame.Flags |= FrameFlags.Unsynchronisation;
+
+				if ((frame.Flags &
+					FrameFlags.TagAlterPreservation) != 0)
+					continue;
+				
+				try {
+					tag_data.Add (frame.Render (
+						header.MajorVersion));
+				} catch (NotImplementedException) {
+				}
+			}
+			
+			// Add unsyncronization bytes if necessary.
+			if (unsynchAtTagLevel)
+				SynchData.UnsynchByteVector (tag_data);
+			
+			// Compute the amount of padding, and append that to
+			// tag_data.
+			
+			
+			if (!has_footer)
+				tag_data.Add (new ByteVector ((int)
+					((tag_data.Count < header.TagSize) ? 
+					(header.TagSize - tag_data.Count) :
+					1024)));
+			
+			// Set the tag size.
+			header.TagSize = (uint) tag_data.Count;
+			
+			tag_data.Insert (0, header.Render ());
+			if (has_footer)
+				tag_data.Add (new Footer (header).Render ());
+			
+			return tag_data;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the header flags applied to the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="HeaderFlags" /> value
+		///    containing flags applied to the current instance.
+		/// </value>
+		public HeaderFlags Flags {
+			get {return header.Flags;}
+			set {header.Flags = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the ID3v2 version of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value specifying the ID3v2 version
+		///    of the current instance.
+		/// </value>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="value" /> is less than 2 or more than 4.
+		/// </exception>
+		public byte Version {
+			get {
+				return ForceDefaultVersion ?
+					DefaultVersion : header.MajorVersion;
+			}
+			set {
+				if (value < 2 || value > 4)
+					throw new ArgumentOutOfRangeException (
+						"value",
+						"Version must be 2, 3, or 4");
+				
+				header.MajorVersion = value;
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Properties
+		
+		/// <summary>
+		///    Gets and sets the ISO-639-2 language code to use when
+		///    searching for and storing language specific values.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing an ISO-639-2
+		///    language code fto use when searching for and storing
+		///    language specific values.
+		/// </value>
+		/// <remarks>
+		///    If the language is unknown, "   " is the appropriate
+		///    filler.
+		/// </remarks>
+		public static string Language {
+			get {return language;}
+			set {
+				language = (value == null || value.Length < 3) ?
+					"   " : value.Substring (0,3);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the the default version to use when
+		///    creating new tags.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value specifying the default ID3v2
+		///    version. The default version for this library is 3.
+		/// </value>
+		/// <remarks>
+		///    If <see cref="ForceDefaultVersion" /> is <see
+		///    langword="true" />, all tags will be rendered with this
+		///    version.
+		/// </remarks>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="value" /> is less than 2 or more than 4.
+		/// </exception>
+		public static byte DefaultVersion {
+			get {return default_version;}
+			set {
+				if (value < 2 || value > 4)
+					throw new ArgumentOutOfRangeException (
+						"value",
+						"Version must be 2, 3, or 4");
+				
+				default_version = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets whether or not to save all tags in the
+		///    default version rather than their original version.
+		/// </summary>
+		/// <value>
+		///    If <see langword="true"/>, tags will be saved in
+		///    <see cref="DefaultVersion" /> rather than their original
+		///    format, with the exception of tags with footers, which
+		///    will be saved in version 4.
+		/// </value>
+		public static bool ForceDefaultVersion {
+			get {return force_default_version;}
+			set {force_default_version = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the encoding to use when creating new
+		///    frames.
+		/// </summary>
+		/// <value>
+		///    A <see cref="StringType" /> value specifying the encoding
+		///    to use when creating new frames.
+		/// </value>
+		public static StringType DefaultEncoding {
+			get {return default_string_type;}
+			set {default_string_type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets whether or not to render all frames with
+		///    the default encoding rather than their original encoding.
+		/// </summary>
+		/// <value>
+		///    If <see langword="true"/>, fames will be rendered in
+		///    <see cref="DefaultEncoding" /> rather than their original
+		///    encoding.
+		/// </value>
+		public static bool ForceDefaultEncoding {
+			get {return force_default_string_type;}
+			set {force_default_string_type = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets whether or not to use ID3v1 style numeric
+		///    genres when possible.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    use genres with numeric values when possible.
+		/// </value>
+		/// <remarks>
+		///    If <see langword="true" />, TagLib# will try looking up
+		///    the numeric genre code when storing the value. For
+		///    ID3v2.2 and ID3v2.3, "Rock" would be stored as "(17)" and
+		///    for ID3v2.4 it would be stored as "17".
+		/// </remarks>
+		public static bool UseNumericGenres {
+			get {return use_numeric_genres;}
+			set {use_numeric_genres = value;}
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates the current instance be reading in a tag from
+		///    a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object to read the tag from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to read the tag.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than 0 or greater
+		///    than the size of the file.
+		/// </exception>
+		protected void Read (File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Mode = File.AccessMode.Read;
+			
+			if (position < 0 || position > file.Length - Header.Size)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			file.Seek (position);
+			
+			header = new Header (file.ReadBlock ((int) Header.Size));
+			
+			// If the tag size is 0, then this is an invalid tag.
+			// Tags must contain at least one frame.
+			
+			if(header.TagSize == 0)
+				return;
+			
+			Parse (file.ReadBlock ((int) header.TagSize));
+		}
+		
+		/// <summary>
+		///    Populates the current instance by parsing the contents of
+		///    a raw ID3v2 tag, minus the header.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the content
+		///    of an ID3v2 tag, minus the header.
+		/// </param>
+		/// <remarks>
+		///    This method must only be called after the internal
+		///    header has been read from the file, otherwise the data
+		///    cannot be parsed correctly.
+		/// </remarks>
+		protected void Parse (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+
+			// If the entire tag is marked as unsynchronized, and this tag
+			// is version id3v2.3 or lower, resynchronize it.
+			bool fullTagUnsynch =  (header.MajorVersion < 4) && ((header.Flags & HeaderFlags.Unsynchronisation) != 0);
+			if (fullTagUnsynch)
+				SynchData.ResynchByteVector (data);
+			
+			int frame_data_position = 0;
+			int frame_data_length = data.Count;
+			
+			// Check for the extended header.
+			
+			if ((header.Flags & HeaderFlags.ExtendedHeader) != 0) {
+				extended_header = new ExtendedHeader (data,
+					header.MajorVersion);
+				
+				if (extended_header.Size <= data.Count) {
+					frame_data_position += (int)
+						extended_header.Size;
+					frame_data_length -= (int)
+						extended_header.Size;
+				}
+			}
+			
+			// Parse the frames. TDRC, TDAT, and TIME will be needed
+			// for post-processing, so check for them as they are
+			// loaded.
+			TextInformationFrame tdrc = null;
+			TextInformationFrame tyer = null;
+			TextInformationFrame tdat = null;
+			TextInformationFrame time = null;
+			
+			while (frame_data_position < frame_data_length -
+				FrameHeader.Size (header.MajorVersion)) {
+				
+				// If the next data is position is 0, assume
+				// that we've hit the padding portion of the
+				// frame data.
+				if(data [frame_data_position] == 0)
+					break;
+				
+				Frame frame = null;
+				
+				try {
+					frame = FrameFactory.CreateFrame (data,
+						ref frame_data_position,
+						header.MajorVersion,
+						fullTagUnsynch);
+				} catch (NotImplementedException) {
+					continue;
+				}
+				
+				if(frame == null)
+					break;
+
+				// Only add frames that contain data.
+				if (frame.Size == 0)
+					continue;
+				
+				AddFrame (frame);
+				
+				// If the tag is version 4, no post-processing
+				// is needed.
+				if (header.MajorVersion == 4)
+					continue;
+					
+				// Load up the first instance of each, for
+				// post-processing.
+				
+				if (tdrc == null &&
+					frame.FrameId.Equals (FrameType.TDRC)) {
+					tdrc = frame as TextInformationFrame;
+				} else if (tyer == null &&
+					frame.FrameId.Equals (FrameType.TYER)) {
+					tyer = frame as TextInformationFrame;
+				} else if (tdat == null &&
+					frame.FrameId.Equals (FrameType.TDAT)) {
+					tdat = frame as TextInformationFrame;
+				} else if (time == null &&
+					frame.FrameId.Equals (FrameType.TIME)) {
+					time = frame as TextInformationFrame;
+				}
+			}
+
+			// Try to fill out the date/time of the TDRC frame.  Can't do that if no TDRC
+			// frame exists, or if there is no TDAT frame, or if TDRC already has the date.
+			if (tdrc == null || tdat == null || tdrc.ToString ().Length > 4) {
+				return;
+			}
+
+			string year = tdrc.ToString ();
+			if (year.Length != 4)
+				return;
+
+			// Start with the year already in TDRC, then add the TDAT and TIME if available
+			StringBuilder tdrc_text = new StringBuilder ();
+			tdrc_text.Append (year);
+
+			// Add the date
+			if (tdat != null) {
+				string tdat_text = tdat.ToString ();
+				if (tdat_text.Length == 4) {
+					tdrc_text.Append ("-").Append (tdat_text, 0, 2)
+						.Append ("-").Append (tdat_text, 2, 2);
+
+					// Add the time
+					if (time != null) {
+						string time_text = time.ToString ();
+							
+						if (time_text.Length == 4)
+							tdrc_text.Append ("T").Append (time_text, 0, 2)
+								.Append (":").Append (time_text, 2, 2);
+
+						RemoveFrames (FrameType.TIME);
+					}
+				}
+
+				RemoveFrames (FrameType.TDAT);
+			}
+
+			tdrc.Text = new string [] { tdrc_text.ToString () };
+		}
+		
+#endregion
+		
+		
+		
+#region Private Methods
+		
+		// TODO: These should become public some day.
+		
+		/// <summary>
+		///    Gets the text value from a specified Text Information
+		///    Frame.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the frame
+		///    identifier of the Text Information Frame to get the value
+		///    from.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the text of the
+		///    specified frame, or <see langword="null" /> if no value
+		///    was found.
+		/// </returns>
+		private string GetTextAsString (ByteVector ident)
+		{
+			TextInformationFrame frame = TextInformationFrame.Get (
+				this, ident, false);
+			
+			string result = frame == null ? null : frame.ToString ();
+			return string.IsNullOrEmpty (result) ? null : result;
+		}
+		
+		/// <summary>
+		///    Gets the text values from a specified Text Information
+		///    Frame.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the frame
+		///    identifier of the Text Information Frame to get the value
+		///    from.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the text of the
+		///    specified frame, or an empty array if no values were
+		///    found.
+		/// </returns>
+		private string [] GetTextAsArray (ByteVector ident)
+		{
+			TextInformationFrame frame = TextInformationFrame.Get (
+				this, ident, false);
+			return frame == null ? new string [0] : frame.Text;
+		}
+		
+		/// <summary>
+		///    Gets an integer value from a "/" delimited list in a
+		///    specified Text Information Frame.
+		/// </summary>
+		/// <param name="ident">
+		///    A <see cref="ByteVector" /> object containing the frame
+		///    identifier of the Text Information Frame to read from.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the index in the
+		///    integer list of the value to return.
+		/// </param>
+		/// <returns>
+		///    A <see cref="uint" /> value read from the list in the
+		///    frame, or 0 if the value wasn't found.
+		/// </returns>
+		private uint GetTextAsUInt32 (ByteVector ident, int index)
+		{
+			string text = GetTextAsString (ident);
+			
+			if (text == null)
+				return 0;
+			
+			string [] values = text.Split (new char [] {'/'},
+				index + 2);
+			
+			if (values.Length < index + 1)
+				return 0;
+			
+			uint result;
+			if (uint.TryParse (values [index], out result))
+				return result;
+			
+			return 0;
+		}
+
+		/// <summary>
+		/// Gets a TXXX frame via reference of the description field
+		/// </summary>
+		/// <param name="description">String containing the description field</param>
+		/// <returns>UserTextInformationFrame (TXXX) that corresponds to the description</returns>
+		private string GetUserTextAsString (string description){
+
+			//Gets the TXXX frame, frame will be null if nonexistant
+			UserTextInformationFrame frame = UserTextInformationFrame.Get (
+				this, description, false);
+
+			//TXXX frames support multivalue strings, join them up and return
+			//only the text from the frame.
+			string result = frame == null ? null : string.Join (";",frame.Text);
+			return string.IsNullOrEmpty (result) ? null : result;
+
+		}
+
+		/// <summary>
+		/// Creates and/or sets a UserTextInformationFrame (TXXX)  with the given
+		/// description and text.
+		/// </summary>
+		/// <param name="description">String containing the Description field for the
+		/// TXXX frame</param>
+		/// <param name="text">String containing the Text field for the TXXX frame</param>
+		private void SetUserTextAsString(string description, string text) {
+
+			//Get the TXXX frame, create a new one if needed
+			UserTextInformationFrame frame = UserTextInformationFrame.Get(
+				this, description, true);
+
+			if (!string.IsNullOrEmpty(text)) {
+				frame.Text = text.Split(';');
+			}
+			else {
+			//Text string is null or empty, delete the frame, prevent empties
+				RemoveFrame(frame);
+			}
+
+		}
+
+		/// <summary>
+		/// Gets the text from a particular UFID frame, referenced by the owner field
+		/// </summary>
+		/// <param name="owner">String containing the "Owner" data</param>
+		/// <returns>String containing the text from the UFID frame, or null</returns>
+		private string GetUfidText(string owner) {
+
+			//Get the UFID frame, frame will be null if nonexistant
+			UniqueFileIdentifierFrame frame = UniqueFileIdentifierFrame.Get(
+				this, owner, false);
+			
+			//If the frame existed: frame.Identifier is a bytevector, get a string
+			string result = frame == null ? null : frame.Identifier.ToString();
+			return string.IsNullOrEmpty (result) ? null : result;
+		}
+
+		/// <summary>
+		/// Creates and/or sets the text for a UFID frame, referenced by owner
+		/// </summary>
+		/// <param name="owner">String containing the Owner field</param>
+		/// <param name="text">String containing the text to set for the frame</param>
+		private void SetUfidText(string owner, string text) {
+
+			//Get a UFID frame, create if necessary
+			UniqueFileIdentifierFrame frame = UniqueFileIdentifierFrame.Get(
+				this, owner, true);
+
+			//If we have a real string, convert to ByteVector and apply to frame
+			if (!string.IsNullOrEmpty(text)) {
+				ByteVector identifier = ByteVector.FromString(text, StringType.UTF8);
+				frame.Identifier = identifier;
+			}
+			else {
+				//String was null or empty, remove the frame to prevent empties
+				RemoveFrame(frame);
+			}
+		}
+
+		/// <summary>
+		///    Moves a specified frame so it is the first of its type in
+		///    the tag.
+		/// </summary>
+		/// <param name="frame">
+		///    A <see cref="Frame" /> object to make the first of its
+		///    type.
+		/// </param>
+		private void MakeFirstOfType (Frame frame)
+		{
+			ByteVector type = frame.FrameId;
+			Frame swapping = null;
+			for (int i = 0; i < frame_list.Count; i ++) {
+				if (swapping == null) {
+					if (frame_list [i].FrameId.Equals (type))
+						swapping = frame;
+					else
+						continue;
+				}
+				
+				Frame tmp = frame_list [i];
+				frame_list [i] = swapping;
+				swapping = tmp;
+				
+				if (swapping == frame)
+					return;
+			}
+			
+			if (swapping != null)
+				frame_list.Add (swapping);
+		}
+		
+		#endregion
+		
+		
+		
+#region IEnumerable
+		
+		/// <summary>
+		///    Gets an enumerator for enumerating through the frames.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the frames.
+		/// </returns>
+		public IEnumerator<Frame> GetEnumerator ()
+		{
+			return frame_list.GetEnumerator ();
+		}
+		
+		IEnumerator IEnumerable.GetEnumerator ()
+		{
+			return frame_list.GetEnumerator ();
+		}
+		
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.Id3v2" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.Id3v2;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TIT2" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string Title {
+			get {return GetTextAsString (FrameType.TIT2);}
+			set {SetTextFrame (FrameType.TIT2, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the Title of the
+		///    media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names for
+		///    the Title of the media described by the current instance,
+		///    or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TSOT" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string TitleSort {
+			get {return GetTextAsString (FrameType.TSOT);}
+			set {SetTextFrame (FrameType.TSOT, value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TPE1" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string [] Performers {
+			get {return GetTextAsArray (FrameType.TPE1);}
+			set {SetTextFrame (FrameType.TPE1, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TSOP" Text
+		///    Information Frame. http://www.id3.org/id3v2.4.0-frames
+		/// </remarks>
+		public override string [] PerformersSort {
+			get {return GetTextAsArray (FrameType.TSOP);}
+			set {SetTextFrame (FrameType.TSOP, value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names of the band or artist who is 
+		///    credited in the creation of the entire album or collection
+		///    containing the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TSO2" Text
+		///    Information Frame. http://www.id3.org/iTunes
+		/// </remarks>
+		public override string [] AlbumArtistsSort {
+			get {return GetTextAsArray (FrameType.TSO2);}
+			set {SetTextFrame (FrameType.TSO2, value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TPE2" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string [] AlbumArtists {
+			get {return GetTextAsArray (FrameType.TPE2);}
+			set {SetTextFrame (FrameType.TPE2, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TCOM" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string [] Composers {
+			get {return GetTextAsArray (FrameType.TCOM);}
+			set {SetTextFrame (FrameType.TCOM, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the composers of the
+		///    media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TSOC" Text
+		///    Information Frame. http://www.id3.org/id3v2.4.0-frames
+		/// </remarks>
+		public override string [] ComposersSort {
+			get {return GetTextAsArray (FrameType.TSOC);}
+			set {SetTextFrame (FrameType.TSOC, value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TALB" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string Album {
+			get {return GetTextAsString (FrameType.TALB);}
+			set {SetTextFrame (FrameType.TALB, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the Album title of the
+		///    media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names for
+		///    the Title in the media described by the current instance,
+		///    or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TSOA" Text
+		///    Information Frame. http://www.id3.org/id3v2.4.0-frames
+		/// </remarks>
+		public override string AlbumSort {
+			get {return GetTextAsString (FrameType.TSOA);}
+			set {SetTextFrame (FrameType.TSOA, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "COMM" Comments
+		///    Frame with an empty description and the language
+		///    specified by <see cref="Language" />.
+		/// </remarks>
+		public override string Comment {
+			get {
+				CommentsFrame f =
+					CommentsFrame.GetPreferred (this,
+						String.Empty, Language);
+				return f != null ? f.ToString () : null;
+			}
+			set {
+				CommentsFrame frame;
+				
+				if (string.IsNullOrEmpty (value)) {
+					while ((frame = CommentsFrame
+						.GetPreferred (this,
+							string.Empty,
+							Language)) != null)
+						RemoveFrame (frame);
+					
+					return;
+				}
+				
+				frame = CommentsFrame.Get (this, String.Empty,
+					Language, true);
+				
+				frame.Text = value;
+				frame.TextEncoding = DefaultEncoding;
+				MakeFirstOfType (frame);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TCON" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string [] Genres {
+			get {
+				string [] text = GetTextAsArray (FrameType.TCON);
+				
+				if (text.Length == 0)
+					return text;
+				
+				List<string> list = new List<string> ();
+				
+				foreach (string genre in text) {
+					if (string.IsNullOrEmpty (genre))
+						continue;
+					
+					// The string may just be a genre
+					// number.
+					
+					string genre_from_index =
+						TagLib.Genres.IndexToAudio (
+							genre);
+					
+					if (genre_from_index != null)
+						list.Add (genre_from_index);
+					else
+						list.Add (genre);
+				}
+				
+				return list.ToArray ();
+			}
+			set {
+				if (value == null || !use_numeric_genres) {
+					SetTextFrame (FrameType.TCON, value);
+					return;
+				}
+				
+				// Clone the array so changes made won't effect
+				// the passed array.
+				value = (string []) value.Clone ();
+				
+				for (int i = 0; i < value.Length; i ++) {
+					int index = TagLib.Genres.AudioToIndex (
+						value [i]);
+					
+					if (index != 255)
+						value [i] = index.ToString (
+							CultureInfo.InvariantCulture);
+				}
+				
+				SetTextFrame (FrameType.TCON, value);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TDRC" Text
+		///    Information Frame. If a value greater than 9999 is set,
+		///    this property will be cleared.
+		/// </remarks>
+		public override uint Year {
+			get {
+				string text = GetTextAsString (FrameType.TDRC);
+				
+				if (text == null || text.Length < 4)
+					return 0;
+				
+				uint value;
+				if (uint.TryParse (text.Substring (0, 4),
+					out value))
+					return value;
+				
+				return 0;
+			}
+			set {
+				if (value > 9999)
+					value = 0;
+				
+				SetNumberFrame (FrameType.TDRC, value, 0);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TRCK" Text
+		///    Information Frame.
+		/// </remarks>
+		public override uint Track {
+			get {return GetTextAsUInt32 (FrameType.TRCK, 0);}
+			set {SetNumberFrame (FrameType.TRCK, value, TrackCount);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TRCK" Text
+		///    Information Frame.
+		/// </remarks>
+		public override uint TrackCount {
+			get {return GetTextAsUInt32 (FrameType.TRCK, 1);}
+			set {SetNumberFrame (FrameType.TRCK, Track, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TPOS" Text
+		///    Information Frame.
+		/// </remarks>
+		public override uint Disc {
+			get {return GetTextAsUInt32 (FrameType.TPOS, 0);}
+			set {SetNumberFrame (FrameType.TPOS, value, DiscCount);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TPOS" Text
+		///    Information Frame.
+		/// </remarks>
+		public override uint DiscCount {
+			get {return GetTextAsUInt32 (FrameType.TPOS, 1);}
+			set {SetNumberFrame (FrameType.TPOS, Disc, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "USLT"
+		///    Unsynchronized Lyrics Frame with an empty description and
+		///    the language specified by <see cref="Language" />.
+		/// </remarks>
+		public override string Lyrics {
+			get {
+				UnsynchronisedLyricsFrame f =
+					UnsynchronisedLyricsFrame.GetPreferred (
+						this, string.Empty, Language);
+				
+				return f != null ? f.ToString () : null;
+			}
+			set {
+				UnsynchronisedLyricsFrame frame;
+				
+				if (string.IsNullOrEmpty (value)) {
+					while ((frame = UnsynchronisedLyricsFrame
+						.GetPreferred (this,
+							string.Empty,
+							Language)) != null)
+						RemoveFrame (frame);
+					
+					return;
+				}
+				
+				frame = UnsynchronisedLyricsFrame.Get (this,
+						String.Empty, Language, true);
+				
+				frame.Text = value;
+				frame.TextEncoding = DefaultEncoding;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TIT1" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string Grouping {
+			get {return GetTextAsString (FrameType.TIT1);}
+			set {SetTextFrame (FrameType.TIT1, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TBPM" Text
+		///    Information Frame.
+		/// </remarks>
+		public override uint BeatsPerMinute {
+			get {
+				string text = GetTextAsString (FrameType.TBPM);
+				
+				if (text == null)
+					return 0;
+				
+				double result;
+				if (double.TryParse (text, out result) &&
+					result >= 0.0)
+					return (uint) Math.Round (result);
+				
+				return 0;
+			}
+			set {SetNumberFrame (FrameType.TBPM, value, 0);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TPE3" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string Conductor {
+			get {return GetTextAsString (FrameType.TPE3);}
+			set {SetTextFrame (FrameType.TPE3, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TCOP" Text
+		///    Information Frame.
+		/// </remarks>
+		public override string Copyright {
+			get {return GetTextAsString (FrameType.TCOP);}
+			set {SetTextFrame (FrameType.TCOP, value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ArtistID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ArtistID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicBrainz Artist Id" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzArtistId {
+			get {return GetUserTextAsString ("MusicBrainz Artist Id");}
+			set {SetUserTextAsString ("MusicBrainz Artist Id",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicBrainz Album Id" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseId {
+			get {return GetUserTextAsString ("MusicBrainz Album Id");}
+			set {SetUserTextAsString ("MusicBrainz Album Id",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseArtistID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseArtistID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicBrainz Album Artist Id" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseArtistId {
+			get {return GetUserTextAsString ("MusicBrainz Album Artist Id");}
+			set {SetUserTextAsString ("MusicBrainz Album Artist Id",value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the MusicBrainz TrackID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    TrackID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "UFID:http://musicbrainz.org"; frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzTrackId {
+		    get { return GetUfidText ("http://musicbrainz.org";);}
+		    set {SetUfidText ("http://musicbrainz.org";, value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz DiscID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    DiscID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicBrainz Disc Id" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzDiscId {
+			get {return GetUserTextAsString ("MusicBrainz Disc Id");}
+			set {SetUserTextAsString ("MusicBrainz Disc Id",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicIP PUID
+		///    for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicIP PUID" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicIpId {
+			get {return GetUserTextAsString ("MusicIP PUID");}
+			set {SetUserTextAsString ("MusicIP PUID",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the Amazon ID (ASIN)
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the Amazon Id
+		///    for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:ASIN" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string AmazonId {
+			get {return GetUserTextAsString ("ASIN");}
+			set {SetUserTextAsString ("ASIN",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseStatus
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseStatus for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicBrainz Album Status" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseStatus {
+			get {return GetUserTextAsString ("MusicBrainz Album Status");}
+			set {SetUserTextAsString ("MusicBrainz Album Status",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseType
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseType for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicBrainz Album Type" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseType {
+			get {return GetUserTextAsString ("MusicBrainz Album Type");}
+			set {SetUserTextAsString ("MusicBrainz Album Type",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseCountry
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseCountry for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TXXX:MusicBrainz Album Release Country" frame.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseCountry {
+			get {return GetUserTextAsString ("MusicBrainz Album Release Country");}
+			set {SetUserTextAsString ("MusicBrainz Album Release Country",value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "APIC" Attached
+		///    Picture Frame.
+		/// </remarks>
+		public override IPicture [] Pictures {
+			get {
+				return new List<AttachedPictureFrame> (
+					GetFrames <AttachedPictureFrame> (
+						FrameType.APIC)).ToArray ();
+			}
+			set {
+				RemoveFrames(FrameType.APIC);
+				
+				if(value == null || value.Length == 0)
+					return;
+				
+				foreach(IPicture picture in value) {
+					AttachedPictureFrame frame =
+						picture as AttachedPictureFrame;
+					
+					if (frame == null)
+						frame = new AttachedPictureFrame (
+							picture);
+					
+					AddFrame (frame);
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance does not
+		///    any values. Otherwise <see langword="false" />.
+		/// </value>
+		public override bool IsEmpty {
+			get {return frame_list.Count == 0;}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			frame_list.Clear ();
+		}
+		
+		/// <summary>
+		///    Gets and sets whether or not the album described by the
+		///    current instance is a compilation.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    album described by the current instance is a compilation.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TCMP" Text
+		///    Information Frame to provide support for a feature of the
+		///    Apple iPod and iTunes products.
+		/// </remarks>
+		public bool IsCompilation {
+			get {
+				string val = GetTextAsString (FrameType.TCMP);
+				return !string.IsNullOrEmpty (val) && val != "0";
+			}
+			set {SetTextFrame (FrameType.TCMP, value ? "1" : null);}
+		}
+		
+		/// <summary>
+		///    Copies the values from the current instance to another
+		///    <see cref="TagLib.Tag" />, optionally overwriting
+		///    existing values.
+		/// </summary>
+		/// <param name="target">
+		///    A <see cref="TagLib.Tag" /> object containing the target
+		///    tag to copy values to.
+		/// </param>
+		/// <param name="overwrite">
+		///    A <see cref="bool" /> specifying whether or not to copy
+		///    values over existing one.
+		/// </param>
+		/// <remarks>
+		///    <para>If <paramref name="target" /> is of type <see
+		///    cref="TagLib.Ape.Tag" /> a complete copy of all values
+		///    will be performed. Otherwise, only standard values will
+		///    be copied.</para>
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="target" /> is <see langword="null" />.
+		/// </exception>
+		public override void CopyTo (TagLib.Tag target, bool overwrite)
+		{
+			if (target == null)
+				throw new ArgumentNullException ("target");
+			
+			TagLib.Id3v2.Tag match = target as TagLib.Id3v2.Tag;
+			
+			if (match == null) {
+				base.CopyTo (target, overwrite);
+				return;
+			}
+			
+			List<Frame> frames = new List<Frame> (frame_list);
+			while (frames.Count > 0) {
+				ByteVector ident = frames [0].FrameId;
+				bool copy = true;
+				if (overwrite) {
+					match.RemoveFrames (ident);
+				} else {
+					foreach (Frame f in match.frame_list)
+						if (f.FrameId.Equals (ident)) {
+							copy = false;
+							break;
+						}
+				}
+				
+				for (int i = 0; i < frames.Count;) {
+					if (frames [i].FrameId.Equals (ident)) {
+						if (copy)
+							match.frame_list.Add (
+								frames [i].Clone ());
+						
+						frames.RemoveAt (i);
+					} else {
+						i ++;
+					}
+				}
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region ICloneable
+		
+		/// <summary>
+		///    Creates a deep copy of the current instance.
+		/// </summary>
+		/// <returns>
+		///    A new <see cref="Tag" /> object identical to the current
+		///    instance.
+		/// </returns>
+		public Tag Clone ()
+		{
+			Tag tag = new Tag ();
+			tag.header = header;
+			if (tag.extended_header != null)
+				tag.extended_header = extended_header.Clone ();
+			
+			foreach (Frame frame in frame_list)
+				tag.frame_list.Add (frame.Clone ());
+			
+			return tag;
+		}
+		
+		object ICloneable.Clone ()
+		{
+			return Clone ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Image/Codec.cs b/lib/TagLib/TagLib/Image/Codec.cs
new file mode 100644
index 0000000..22598ba
--- /dev/null
+++ b/lib/TagLib/TagLib/Image/Codec.cs
@@ -0,0 +1,145 @@
+//
+// Codec.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Image
+{
+	/// <summary>
+	///    A photo codec. Contains basic photo details.
+	/// </summary>
+	public abstract class Codec : IPhotoCodec
+	{
+#region Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		public TimeSpan Duration { get { return TimeSpan.Zero; } }
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="MediaTypes" /> containing
+		///    the types of media represented by the current instance.
+		/// </value>
+		public MediaTypes MediaTypes { get { return MediaTypes.Photo; } }
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public abstract string Description { get; }
+		
+		/// <summary>
+		///    Gets the width of the photo represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the width of the
+		///    photo represented by the current instance.
+		/// </value>
+		public int PhotoWidth  { get; protected set; }
+		
+		/// <summary>
+		///    Gets the height of the photo represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the height of the
+		///    photo represented by the current instance.
+		/// </value>
+		public int PhotoHeight { get; protected set; }
+
+		/// <summary>
+		///    Gets the (format specific) quality indicator of the photo
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value indicating the quality. A value
+		///    0 means that there was no quality indicator for the format
+		///    or the file.
+		/// </value>
+		public int PhotoQuality { get; protected set; }
+
+#endregion
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs a new <see cref="Codec" /> with the given width
+		///    and height.
+		/// </summary>
+		/// <param name="width">
+		///    The width of the photo.
+		/// </param>
+		/// <param name="height">
+		///    The height of the photo.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="Codec" /> instance.
+		/// </returns>
+		public Codec (int width, int height) : this (width, height, 0)
+		{	
+		}
+
+		/// <summary>
+		///    Constructs a new <see cref="Codec" /> with the given width
+		///    and height.
+		/// </summary>
+		/// <param name="width">
+		///    The width of the photo.
+		/// </param>
+		/// <param name="height">
+		///    The height of the photo.
+		/// </param>
+		/// <param name="quality">
+		///    The quality indicator for the photo, if the format supports it.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="Codec" /> instance.
+		/// </returns>
+		public Codec (int width, int height, int quality)
+		{
+			PhotoWidth = width;
+			PhotoHeight = height;
+			PhotoQuality = quality;
+		}
+
+#endregion
+		
+	}
+}
diff --git a/lib/TagLib/TagLib/Image/CombinedImageTag.cs b/lib/TagLib/TagLib/Image/CombinedImageTag.cs
new file mode 100644
index 0000000..619f27f
--- /dev/null
+++ b/lib/TagLib/TagLib/Image/CombinedImageTag.cs
@@ -0,0 +1,533 @@
+//
+// CombinedImageTag.cs: The class provides an abstraction to combine
+// ImageTags.
+//
+// Author:
+//   Mike Gemuende (mike gemuende de)
+//   Paul Lange (palango gmx de)
+//
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+using TagLib.IFD;
+using TagLib.Xmp;
+
+namespace TagLib.Image
+{
+	
+	/// <summary>
+	///    Combines some <see cref="ImageTag"/> instance to behave as one.
+	/// </summary>
+	public class CombinedImageTag : ImageTag
+	{
+
+#region Private Fields
+		
+		/// <summary>
+		///    Direct access to the Exif (IFD) tag (if any)
+		/// </summary>
+		public IFDTag Exif { get; private set; }
+
+		/// <summary>
+		///    Direct access to the Xmp tag (if any)
+		/// </summary>
+		public XmpTag Xmp { get; private set; }
+
+		/// <summary>
+		///    Other image tags available in this tag.
+		/// </summary>
+		public List<ImageTag> OtherTags { get; private set; }
+		
+		/// <summary>
+		///    Stores the types of the tags, which are allowed for
+		///    the current instance.
+		/// </summary>
+		internal TagTypes AllowedTypes { get; private set; }
+
+		/// <summary>
+		///    Returns all image tags in this tag, with XMP
+		///    and Exif first.
+		/// </summary>
+		public List<ImageTag> AllTags {
+			get {
+				if (all_tags == null) {
+					all_tags = new List<ImageTag> ();
+					if (Xmp != null)
+						all_tags.Add (Xmp);
+					if (Exif != null)
+						all_tags.Add (Exif);
+					all_tags.AddRange (OtherTags);
+				}
+
+				return all_tags;
+			}
+		}
+
+		private List<ImageTag> all_tags = null;
+		
+#endregion
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="CombinedImageTag" /> with a restriction on the
+		///    allowed tag types contained in this combined tag.
+		/// </summary>
+		/// <param name="allowed_types">
+		///    A <see cref="TagTypes" /> value, which restricts the
+		///    types of metadata that can be contained in this
+		///    combined tag.
+		/// </param>
+		public CombinedImageTag (TagTypes allowed_types)
+		{
+			AllowedTypes = allowed_types;
+			OtherTags = new List<ImageTag> ();
+		}
+		
+#endregion
+		
+#region Protected Methods
+		
+		internal void AddTag (ImageTag tag)
+		{
+			if ((tag.TagTypes & AllowedTypes) != tag.TagTypes)
+				throw new Exception (String.Format ("Attempted to add {0} to an image, but the only allowed types are {1}", tag.TagTypes, AllowedTypes));
+
+			if (tag is IFDTag)
+				Exif = tag as IFDTag;
+			else if (tag is XmpTag)
+				Xmp = tag as XmpTag;
+			else
+				OtherTags.Add (tag);
+
+			all_tags = null;
+		}
+		
+		internal void RemoveTag (ImageTag tag)
+		{
+			if (tag is IFDTag)
+				Exif = null;
+			else if (tag is XmpTag)
+				Xmp = null;
+			else
+				OtherTags.Remove (tag);
+
+			all_tags = null;
+		}
+		
+#endregion
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" />
+		///    containing the tag types contained in the current
+		///    instance.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {
+				TagTypes types = TagTypes.None;
+
+				foreach (ImageTag tag in AllTags)
+					types |= tag.TagTypes;
+
+				return types;
+			}
+		}
+		
+		/// <summary>
+		///    Clears all of the child tags.
+		/// </summary>
+		public override void Clear ()
+		{
+			foreach (ImageTag tag in AllTags)
+				tag.Clear ();
+		}
+		
+		/// <summary>
+		///    Gets or sets the comment for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the comment of the
+		///    current instace.
+		/// </value>
+		public override string Comment {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					string value = tag.Comment;
+					if (!string.IsNullOrEmpty (value))
+						return value;
+				}
+				
+				return String.Empty;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Comment = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the keywords for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the keywords of the
+		///    current instace.
+		/// </value>
+		public override string[] Keywords {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					string[] value = tag.Keywords;
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] {};
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Keywords = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the rating for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> containing the rating of the
+		///    current instace.
+		/// </value>
+		public override uint? Rating {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					uint? value = tag.Rating;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Rating = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the time when the image, the current instance
+		///    belongs to, was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the time the image was taken.
+		/// </value>
+		public override DateTime? DateTime {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					DateTime? value = tag.DateTime;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.DateTime = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the orientation of the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Image.ImageOrientation" /> containing the orienatation of the
+		///    image
+		/// </value>
+		public override ImageOrientation Orientation {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					ImageOrientation value = tag.Orientation;
+					
+					if ((uint) value >= 1U && (uint) value <= 8U)
+						return value;
+				}
+				
+				return 0;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Orientation = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the software the image, the current instance
+		///    belongs to, was created with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the name of the
+		///    software the current instace was created with.
+		/// </value>
+		public override string Software {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					string value = tag.Software;
+					
+					if (!string.IsNullOrEmpty(value))
+						return value;
+				}
+				
+				return null;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Software = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the latitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the latitude ranging from -90.0
+		///    to +90.0 degrees.
+		/// </value>
+		public override double? Latitude {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					double? value = tag.Latitude;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Latitude = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the longitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the longitude ranging from -180.0 
+		///    to +180.0 degrees.
+		/// </value>
+		public override double? Longitude {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					double? value = tag.Longitude;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Longitude = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the altitude of the GPS coordinate the current
+		///    image was taken. The unit is meter.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the altitude. A positive value
+		///    is above sea level, a negative one below sea level. The unit is meter.
+		/// </value>
+		public override double? Altitude {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					double? value = tag.Altitude;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+			set {
+				foreach (ImageTag tag in AllTags)
+					tag.Altitude = value;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the exposure time the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the exposure time in seconds.
+		/// </value>
+		public override double? ExposureTime {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					double? value = tag.ExposureTime;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the FNumber the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the FNumber.
+		/// </value>
+		public override double? FNumber {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					double? value = tag.FNumber;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the ISO speed the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the ISO speed as defined in ISO 12232.
+		/// </value>
+		public override uint? ISOSpeedRatings {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					uint? value = tag.ISOSpeedRatings;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the focal length the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the focal length in millimeters.
+		/// </value>
+		public override double? FocalLength {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					double? value = tag.FocalLength;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the focal length the image, the current instance belongs
+		///    to, was taken with, assuming a 35mm film camera.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the focal length in 35mm equivalent in millimeters.
+		/// </value>
+		public override uint? FocalLengthIn35mmFilm {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					uint? value = tag.FocalLengthIn35mmFilm;
+					
+					if (value != null)
+						return value;
+				}
+				
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the manufacture of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the manufacture name.
+		/// </value>
+		public override string Make {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					string value = tag.Make;
+					
+					if (!string.IsNullOrEmpty(value))
+						return value;
+				}
+				
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the model name of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the model name.
+		/// </value>
+		public override string Model {
+			get {
+				foreach (ImageTag tag in AllTags) {
+					string value = tag.Model;
+					
+					if (value != null)
+					if (!string.IsNullOrEmpty(value))
+						return value;
+				}
+				
+				return null;
+			}
+		}
+#endregion
+		
+	}
+}
diff --git a/lib/TagLib/TagLib/Image/File.cs b/lib/TagLib/TagLib/Image/File.cs
new file mode 100644
index 0000000..c76ee75
--- /dev/null
+++ b/lib/TagLib/TagLib/Image/File.cs
@@ -0,0 +1,185 @@
+//
+// File.cs: Base class for Image types.
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+using TagLib.Jpeg;
+using TagLib.IFD;
+using TagLib.Xmp;
+
+namespace TagLib.Image
+{
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide basic
+	///    functionality common to all image types.
+	/// </summary>
+	public abstract class File : TagLib.File
+	{
+		private CombinedImageTag image_tag;
+
+#region Constructors		
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		protected File (string path) : base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		protected File (IFileAbstraction abstraction) : base (abstraction)
+		{
+		}
+		
+#endregion
+
+#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override Tag Tag { get { return ImageTag; } }
+
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Image.CombinedImageTag" /> object 
+		///    representing all image tags stored in the current instance.
+		/// </value>
+		public CombinedImageTag ImageTag {
+			get { return image_tag; }
+			protected set { image_tag = value; }
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags (TagLib.TagTypes types)
+		{
+			List<ImageTag> to_delete = new List<ImageTag> ();
+
+			foreach (ImageTag tag in ImageTag.AllTags) {
+				if ((tag.TagTypes & types) == tag.TagTypes)
+					to_delete.Add (tag);
+			}
+
+			foreach (ImageTag tag in to_delete)
+				ImageTag.RemoveTag (tag);
+		}
+
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public override TagLib.Tag GetTag (TagLib.TagTypes type,
+		                                   bool create)
+		{
+			foreach (Tag tag in ImageTag.AllTags) {
+				if ((tag.TagTypes & type) == type)
+					return tag;
+			}
+
+			if (!create || (type & ImageTag.AllowedTypes) == 0)
+				return null;
+
+			ImageTag new_tag = null;
+			switch (type) {
+			case TagTypes.JpegComment:
+				new_tag = new JpegCommentTag ();
+				break;
+				
+			case TagTypes.TiffIFD:
+				new_tag = new IFDTag ();
+				break;
+				
+			case TagTypes.XMP:
+				new_tag = new XmpTag ();
+				break;
+			}
+
+			if (new_tag != null) {
+				ImageTag.AddTag (new_tag);
+				return new_tag;
+			}
+
+			throw new NotImplementedException (String.Format ("Adding tag of type {0} not supported!", type));
+		}
+
+#endregion
+
+	}
+}
diff --git a/lib/TagLib/TagLib/Image/ImageOrientation.cs b/lib/TagLib/TagLib/Image/ImageOrientation.cs
new file mode 100644
index 0000000..ce7c2a8
--- /dev/null
+++ b/lib/TagLib/TagLib/Image/ImageOrientation.cs
@@ -0,0 +1,87 @@
+//
+// ImageOrientation.cs: Enum for the orientation of an image
+//
+// Author:
+//   Paul Lange (palango gmx de)
+//
+// Copyright (C) 2009 Paul Lange
+//
+// 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
+
+using System;
+
+namespace TagLib.Image
+{
+	/**
+	
+	  1        2       3      4         5            6           7          8
+	
+	888888  888888      88  88      8888888888  88                  88  8888888888
+	88          88      88  88      88  88      88  88          88  88      88  88
+	8888      8888    8888  8888    88          8888888888  8888888888          88
+	88          88      88  88
+	88          88  888888  888888
+	
+	t-l     t-r     b-r     b-l     l-t         r-t         r-b             l-b
+	
+	**/
+	
+	/// <summary>
+	/// Describes the orientation of an image.
+	/// Values are viewed in terms of rows and columns.
+	/// </summary>
+	public enum ImageOrientation : uint
+	{
+		/// <summary>
+		/// No need to do any transformations.
+		/// </summary>
+		TopLeft = 1,
+		
+		/// <summary>
+		/// Mirror image vertically.
+		/// </summary>
+		TopRight = 2,
+		
+		/// <summary>
+		/// Rotate image 180 degrees.
+		/// </summary>
+		BottomRight = 3,
+		
+		/// <summary>
+		/// Mirror image horizontally
+		/// </summary>
+		BottomLeft = 4,
+		
+		/// <summary>
+		/// Mirror image horizontally and rotate 90 degrees clockwise.
+		/// </summary>
+		LeftTop = 5,
+		
+		/// <summary>
+		/// Rotate image 90 degrees clockwise.
+		/// </summary>
+		RightTop = 6,
+		
+		/// <summary>
+		/// Mirror image vertically and rotate 90 degrees clockwise.
+		/// </summary>
+		RightBottom = 7,
+		
+		/// <summary>
+		/// Rotate image 270 degrees clockwise.
+		/// </summary>
+		LeftBottom = 8
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Image/ImageTag.cs b/lib/TagLib/TagLib/Image/ImageTag.cs
new file mode 100644
index 0000000..60dda0c
--- /dev/null
+++ b/lib/TagLib/TagLib/Image/ImageTag.cs
@@ -0,0 +1,222 @@
+//
+// ImageTag.cs: This abstract class extends the Tag class by basic Image
+// properties.
+//
+// Author:
+//   Mike Gemuende (mike gemuende de)
+//   Paul Lange (palango gmx de)
+//
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Image
+{
+	
+	/// <summary>
+	///    A class to abstract the image tags. It extends the <see cref="Tag"/>
+	///    class and adds some image specific propties.
+	/// </summary>
+	public abstract class ImageTag : Tag
+	{
+
+#region Public Properties
+	
+		/// <summary>
+		///    Gets or sets the keywords for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the keywords of the
+		///    current instace.
+		/// </value>
+		public virtual string[] Keywords {
+			get { return new string [] {}; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the rating for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> containing the rating of the
+		///    current instace.
+		/// </value>
+		public virtual uint? Rating {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the time when the image, the current instance
+		///    belongs to, was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the time the image was taken.
+		/// </value>
+		public virtual DateTime? DateTime {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the orientation of the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Image.ImageOrientation" /> containing the orientation of the
+		///    image
+		/// </value>
+		public virtual ImageOrientation Orientation {
+			get { return 0; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the software the image, the current instance
+		///    belongs to, was created with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the name of the
+		///    software the current instace was created with.
+		/// </value>
+		public virtual string Software {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the latitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the latitude ranging from -90.0
+		///    to +90.0 degrees.
+		/// </value>
+		public virtual double? Latitude {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the longitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the longitude ranging from -180.0 
+		///    to +180.0 degrees.
+		/// </value>
+		public virtual double? Longitude {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the altitude of the GPS coordinate the current
+		///    image was taken. The unit is meter.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the altitude. A positive value
+		///    is above sea level, a negative one below sea level. The unit is meter.
+		/// </value>
+		public virtual double? Altitude {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets the exposure time the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the exposure time in seconds.
+		/// </value>
+		public virtual double? ExposureTime {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the FNumber the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the FNumber.
+		/// </value>
+		public virtual double? FNumber {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the ISO speed the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the ISO speed as defined in ISO 12232.
+		/// </value>
+		public virtual uint? ISOSpeedRatings {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the focal length the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the focal length in millimeters.
+		/// </value>
+		public virtual double? FocalLength {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the focal length the image, the current instance belongs
+		///    to, was taken with, assuming a 35mm film camera.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the focal length in 35mm equivalent in millimeters.
+		/// </value>
+		public virtual uint? FocalLengthIn35mmFilm {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the manufacture of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the manufacture name.
+		/// </value>
+		public virtual string Make {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the model name of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the model name.
+		/// </value>
+		public virtual string Model {
+			get { return null; }
+		}
+#endregion
+		
+	}
+}
diff --git a/lib/TagLib/TagLib/IntList.cs b/lib/TagLib/TagLib/IntList.cs
new file mode 100644
index 0000000..49998c1
--- /dev/null
+++ b/lib/TagLib/TagLib/IntList.cs
@@ -0,0 +1,20 @@
+/***************************************************************************
+    copyright            : (C) 2006 Novell, Inc.
+    email                : Aaron Bockover <abockover novell 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                                                                   *
+ ***************************************************************************/
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Jpeg/Codec.cs b/lib/TagLib/TagLib/Jpeg/Codec.cs
new file mode 100644
index 0000000..cedc281
--- /dev/null
+++ b/lib/TagLib/TagLib/Jpeg/Codec.cs
@@ -0,0 +1,66 @@
+//
+// Codec.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Jpeg
+{
+	/// <summary>
+	///    A Jpeg photo codec. Contains basic photo details.
+	/// </summary>
+	public class Codec : Image.Codec
+	{
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public override string Description { get { return "JFIF File"; } }
+
+		
+		/// <summary>
+		///    Constructs a new <see cref="Codec" /> with the given width
+		///    and height.
+		/// </summary>
+		/// <param name="width">
+		///    The width of the photo.
+		/// </param>
+		/// <param name="height">
+		///    The height of the photo.
+		/// </param>
+		/// <param name="quality">
+		///    The quality of the photo.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="Codec" /> instance.
+		/// </returns>
+		public Codec (int width, int height, int quality)
+			: base (width, height, quality) {}
+	}
+}
diff --git a/lib/TagLib/TagLib/Jpeg/File.cs b/lib/TagLib/TagLib/Jpeg/File.cs
new file mode 100644
index 0000000..6bdb451
--- /dev/null
+++ b/lib/TagLib/TagLib/Jpeg/File.cs
@@ -0,0 +1,812 @@
+//
+// File.cs: Provides tagging for Jpeg files
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Mike Gemuende (mike gemuende de)
+//   Stephane Delcroix (stephane delcroix org)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (C) 2009 Mike Gemuende
+// Copyright (c) 2009 Stephane Delcroix
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using TagLib.Image;
+using TagLib.IFD;
+using TagLib.IFD.Entries;
+using TagLib.Xmp;
+
+namespace TagLib.Jpeg
+{
+
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide tagging
+	///    and properties support for Jpeg files.
+	/// </summary>
+	[SupportedMimeType("taglib/jpg", "jpg")]
+	[SupportedMimeType("taglib/jpeg", "jpeg")]
+	[SupportedMimeType("taglib/jpe", "jpe")]
+	[SupportedMimeType("taglib/jif", "jif")]
+	[SupportedMimeType("taglib/jfif", "jfif")]
+	[SupportedMimeType("taglib/jfi", "jfi")]
+	[SupportedMimeType("image/jpeg")]
+	public class File : TagLib.Image.File
+	{
+		
+		/// <summary>
+		///    The magic bits used to recognize an Exif segment
+		/// </summary>
+		private static readonly string EXIF_IDENTIFIER = "Exif\0\0";
+		
+		/// <summary>
+		///    Standard (empty) JFIF header to add, if no one is contained
+		/// </summary>
+		private static readonly byte [] BASIC_JFIF_HEADER = new byte [] {
+			// segment maker
+			0xFF, (byte) Marker.APP0,
+			
+			// segment size
+			0x00, 0x10,
+			
+			// segment data
+			0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01,
+			0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00
+		};
+
+		
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the media properties.
+		/// </summary>
+		private Properties properties;
+		
+		/// <summary>
+		///    Stores the size of the metadata block at the beginning of the file.
+		///    The value is used to know which data block should be overwritten when
+		///    metadata is saved.
+		/// </summary>
+		/// <remarks>
+		///    If some metadata is not contained at the beginning of the file,
+		///    <see cref="exif_position"/>, <see cref="xmp_position"/> and 
+		///    <see cref="comment_position"/> stores the offset to the metdata segments.
+		///    Those segments are deleted when metadata is saved and the metdata is
+		///    completely written at the beginning of the file.
+		/// </remarks>
+		private long metadata_length = 0;
+		
+		/// <summary>
+		///    Contains the position of the Exif segment, if it is not contained in
+		///    metadata segment, to delete it. <see cref="metadata_length"/>
+		/// </summary>	
+		private long exif_position = 0;
+		
+		/// <summary>
+		///    Contains the position of the Xmp segment, if it is not contained in
+		///    metadata segment, to delete it. <see cref="metadata_length"/>
+		/// </summary>	
+		private long xmp_position = 0;
+		
+		/// <summary>
+		///    Contains the position of the Comment segment, if it is not contained in
+		///    metadata segment, to delete it. <see cref="metadata_length"/>
+		/// </summary>		
+		private long comment_position = 0;
+
+		/// <summary>
+		///    For now, we do not allow to change the jfif header. As long as this is
+		///    the case, the header is kept as it is.
+		/// </summary>
+		private ByteVector jfif_header = null;
+		
+		/// <summary>
+		///    The image width, as parsed from the Frame
+		/// </summary>
+		ushort width;
+
+		/// <summary>
+		///    The image height, as parsed from the Frame
+		/// </summary>
+		ushort height;
+
+		/// <summary>
+		///    Quality of the image, stored as we parse the file 
+		/// </summary>
+		int quality;
+		
+#endregion		
+		
+#region Constructors		
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: this (new File.LocalFileAbstraction (path),
+				propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : this (path, ReadStyle.Average)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle) : base (abstraction)
+		{
+			Read (propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		protected File (IFileAbstraction abstraction)
+			: this (abstraction, ReadStyle.Average)
+		{
+		}
+		
+#endregion
+
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties {
+			get { return properties; }
+		}
+		
+#endregion
+		
+#region Public Methods
+
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			Mode = AccessMode.Write;
+			try {
+				WriteMetadata ();
+				
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+#endregion
+
+#region Private Methods
+
+		/// <summary>
+		///    Reads the information from file with a specified read style.
+		/// </summary>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		private void Read (ReadStyle propertiesStyle)
+		{
+			Mode = AccessMode.Read;
+			try {
+				ImageTag = new CombinedImageTag (TagTypes.XMP | TagTypes.TiffIFD | TagTypes.JpegComment);
+				
+				ValidateHeader ();
+				ReadMetadata ();
+				
+				TagTypesOnDisk = TagTypes;
+				
+				if (propertiesStyle != ReadStyle.None)
+					properties = ExtractProperties ();
+				
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Attempts to extract the media properties of the main
+		///    photo.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="Properties" /> object with a best effort guess
+		///    at the right values. When no guess at all can be made,
+		///    <see langword="null" /> is returned.
+		/// </returns>
+		private Properties ExtractProperties ()
+		{
+			if (width > 0 && height > 0) 
+				return new Properties (TimeSpan.Zero, new Codec (width, height, quality));
+
+			return null;
+
+		}
+		
+		/// <summary>
+		///    Validates if the opened file is actually a JPEG.
+		/// </summary>
+		private void ValidateHeader ()
+		{
+			ByteVector segment = ReadBlock (2);
+			if (segment.ToUShort () != 0xFFD8)
+				throw new CorruptFileException ("Expected SOI marker at the start of the file.");
+		}
+		
+		
+		/// <summary>
+		///    Reads a segment marker for a segment starting at current position.
+		///    The second byte of the marker is returned, since the first is equal
+		///    to 0xFF in every case.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="TagLib.Jpeg.Marker"/> with the second byte of the segment marker.
+		/// </returns>
+		private Marker ReadSegmentMarker ()
+		{			
+			ByteVector segment_header = ReadBlock (2);
+			
+			if (segment_header.Count != 2)
+				throw new CorruptFileException ("Could not read enough bytes for segment maker");
+			
+			if (segment_header[0] != 0xFF)
+				throw new CorruptFileException ("Start of Segment expected at " + (Tell - 2));
+			
+			return (Marker)segment_header[1];
+		}
+		
+		
+		/// <summary>
+		///    Reads the size of a segment at the current position.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.UInt16"/> with the size of the current segment.
+		/// </returns>
+		private ushort ReadSegmentSize ()
+		{
+			long position = Tell;
+			
+			ByteVector segment_size_bytes = ReadBlock (2);
+			
+			if (segment_size_bytes.Count != 2)
+				throw new CorruptFileException ("Could not read enough bytes to determine segment size");
+			
+			ushort segment_size = segment_size_bytes.ToUShort ();
+			
+			// the size itself must be contained in the segment size
+			// so the smallest (theoretically) possible number of bytes if 2
+			if (segment_size < 2)
+				throw new CorruptFileException (String.Format ("Invalid segment size ({0} bytes)", segment_size));
+			
+			if (position + segment_size >= Length)
+				throw new CorruptFileException ("Segment size exceeds file size");
+			
+			return segment_size;
+		}
+		
+		
+		/// <summary>
+		///    Extracts the metadata from the current file by reading every segment in file.
+		///    Method should be called with read position at first segment marker.
+		/// </summary>
+		private void ReadMetadata ()
+		{
+			bool advance_metdata = true;
+			
+			// loop while marker is not EOI and not the data segment
+			while (true) {
+				Marker marker = ReadSegmentMarker ();
+				
+				// we stop parsing when the end of file (EOI) or the begin of the
+				// data segment is reached (SOS)
+				// the second case is a trade-off between tolerant and fast parsing
+			 	if (marker == Marker.EOI || marker == Marker.SOS)
+					break;
+
+				long position = Tell;
+				ushort segment_size = ReadSegmentSize ();
+				
+				// segment size contains 2 bytes of the size itself, so the
+				// pure data size is this (and the cast is save)
+				ushort data_size = (ushort) (segment_size - 2);
+				
+				// stores, if metadata was read
+				bool is_metadata = false;
+				
+				switch (marker) {
+				case Marker.APP0:	// possibly JFIF header
+					is_metadata = ReadJFIFHeader (data_size);
+					break;
+				
+				case Marker.APP1:	// possibly Exif or Xmp data found
+					is_metadata = ReadAPP1Segment (data_size);
+					break;
+
+				case Marker.COM:	// Comment segment found
+					is_metadata = ReadCOMSegment (data_size);
+					break;
+
+				case Marker.SOF0:
+				case Marker.SOF1:
+				case Marker.SOF2:
+				case Marker.SOF3:
+				case Marker.SOF9:
+				case Marker.SOF10:
+				case Marker.SOF11:
+					is_metadata = ReadSOFSegment (data_size, marker);
+					break;
+
+				case Marker.DQT:	// Quantization table(s), use it to guess quality
+					is_metadata = ReadDQTSegment (data_size);
+					break;
+				}
+
+				// if metadata was read in current segment and all previous segments
+				// are metadata segments (or the JFIF header), then we add current
+				// segment to metadata-block
+				if (is_metadata && advance_metdata)
+					metadata_length += 2 + segment_size; // marker size + segment_size
+				else
+					advance_metdata = false;
+
+				// set position to next segment and start with next segment marker
+				Seek (position + segment_size, SeekOrigin.Begin);
+			}
+		}
+				
+		/// <summary>
+		///    Reads a JFIF header at current position
+		/// </summary>
+		private bool ReadJFIFHeader (ushort length)
+		{
+			// JFIF header should be contained as first segment
+			// SOI marker + APP0 Marker + segment size = 6 bytes
+			if (Tell != 6)
+				return false;
+			
+			if (ReadBlock (5).ToString ().Equals ("JFIF\0")) {
+				
+				// store the JFIF header as it is
+				Seek (2, SeekOrigin.Begin);
+				jfif_header = ReadBlock (length + 2 + 2);
+				
+				return true;
+			}
+			
+			return false;
+		}
+
+		/// <summary>
+		///    Reads an APP1 segment to find EXIF or XMP metadata.
+		/// </summary>
+		/// <param name="length">
+		///    The length of the segment that will be read.
+		/// </param>
+		private bool ReadAPP1Segment (ushort length)
+		{
+			long position = Tell;
+			ByteVector data = null;
+			
+			// for an Exif segment, the data block consists of 14 bytes of:
+			//    * 6 bytes Exif identifier string
+			//    * 2 bytes bigendian indication MM (or II)
+			//    * 2 bytes Tiff magic number (42)
+			//    * 4 bytes offset of the first IFD in this segment
+			//
+			//    the last two points are alreay encoded according to
+			//    big- or littleendian
+			int exif_header_length = 14;
+			
+			// could be an Exif segment
+			if (exif_position == 0 && length >= exif_header_length) {
+				
+				data = ReadBlock (exif_header_length);
+				
+				if (data.Count == exif_header_length
+				    && data.Mid (0, 6).ToString ().Equals (EXIF_IDENTIFIER)) {
+					
+					bool is_bigendian = data.Mid (6, 2).ToString ().Equals ("MM");
+					
+					ushort magic = data.Mid (8, 2).ToUShort (is_bigendian);
+					if (magic != 42)
+						throw new Exception (String.Format ("Invalid TIFF magic: {0}", magic));
+					
+					uint ifd_offset = data.Mid (10, 4).ToUInt (is_bigendian);
+					
+					var exif = new IFDTag ();
+					var reader = new IFDReader (this, is_bigendian, exif.Structure, position + 6, ifd_offset, (uint) (length - 6));
+					reader.Read ();
+					ImageTag.AddTag (exif);
+					exif_position = position;
+
+					return true;
+				}
+			}
+			
+			int xmp_header_length = XmpTag.XAP_NS.Length + 1;
+			
+			// could be an Xmp segment
+			if (xmp_position == 0 && length >= xmp_header_length) {
+				
+				// if already data is read for determining the Exif segment,
+				// just read the remaining bytes.
+				// NOTE: that (exif_header_length < xmp_header_length) holds
+				if (data == null)
+					data = ReadBlock (xmp_header_length);
+				else
+					data.Add (ReadBlock (xmp_header_length - exif_header_length));
+				
+				if (data.ToString ().Equals (XmpTag.XAP_NS + "\0")) {
+					ByteVector xmp_data = ReadBlock (length - xmp_header_length);
+					
+					ImageTag.AddTag (new XmpTag (xmp_data.ToString ()));
+					xmp_position = position;
+					
+					return true;
+				}
+			}
+			
+			return false;
+		}
+		
+		/// <summary>
+		///    Deletes a Segment starting at given position
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="System.Int64"/> with the position of the first byte
+		///    of the segment marker (0xFF)
+		/// </param>
+		private void DeleteSegment (long start)
+		{
+			Seek (start, SeekOrigin.Begin);
+			
+			ReadSegmentMarker ();
+			ushort length = ReadSegmentSize ();
+			
+			Insert (new ByteVector (), start, length + 2);
+		}
+		
+		/// <summary>
+		///    Writes the metadata back to file. All metadata is stored in the first segments
+		///    of the file.
+		/// </summary>
+		private void WriteMetadata ()
+		{
+			// first render all metadata segments to a ByteVector before the
+			// file is touched ...
+			ByteVector data = new ByteVector ();
+			
+			// existing jfif header is retained, otherwise a standard one
+			// is created
+			if (jfif_header != null)
+				data.Add (jfif_header);
+			else
+				data.Add (BASIC_JFIF_HEADER);
+			
+			data.Add (RenderExifSegment ());
+			data.Add (RenderXMPSegment ());
+			data.Add (RenderCOMSegment ());
+			
+			// ... then delete the metadata which is not contained in the
+			// metadata block at the beginning of the file
+			// not the efficentest way, but probably it is never really called
+			long[] metadata_positions = {comment_position, xmp_position, exif_position};
+			
+			// start with greatest offset, because this do not affect the smaller ones
+			Array.Sort<long> (metadata_positions);
+			for (int i = metadata_positions.Length - 1; i >= 0; i--) {
+				long metadata_position = metadata_positions[i];
+				
+				// delete data which is not contained in the metdata segment
+				// at the beginning of the file
+				if (2 + metadata_length < metadata_position)
+					DeleteSegment (metadata_position - 4);
+				else
+					break;
+			}
+			
+			// reset it, to not delete it the next time the metadata is saved
+			comment_position = 0;
+			xmp_position = 0;
+			exif_position = 0;
+			
+			// Insert metadata block at the beginning of the file by overwriting
+			// all segments at the beginning already containing metadata
+			Insert (data, 2, metadata_length);
+			
+			metadata_length = data.Count;
+		}
+		
+		/// <summary>
+		///    Creates a <see cref="ByteVector"/> for the Exif segment of this file
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the whole Exif segment, if exif tags
+		///    exists, otherwise null.
+		/// </returns>
+		private ByteVector RenderExifSegment ()
+		{
+			// Check, if IFD0 is contained
+			IFDTag exif = ImageTag.Exif;
+			if (exif == null)
+				return null;
+			
+			// first IFD starts at 8
+			uint first_ifd_offset = 8;
+			
+			// Render IFD0
+			// FIXME: store endianess and use it here
+			var renderer = new IFDRenderer (true, exif.Structure, first_ifd_offset);
+			ByteVector exif_data = renderer.Render ();
+			
+			uint segment_size = (uint) (first_ifd_offset + exif_data.Count + 2 + 6);
+			
+			// do not render data segments, which cannot fit into the possible segment size
+			if (segment_size > ushort.MaxValue)
+				throw new Exception ("Exif Segment is too big to render");
+			
+			// Create whole segment
+			ByteVector data = new ByteVector (new byte [] { 0xFF, (byte) Marker.APP1 });
+			data.Add (ByteVector.FromUShort ((ushort) segment_size));
+			data.Add ("Exif\0\0");
+			data.Add (ByteVector.FromString ("MM", StringType.Latin1));
+			data.Add (ByteVector.FromUShort (42));
+			data.Add (ByteVector.FromUInt (first_ifd_offset));
+			
+			// Add ifd data itself
+			data.Add (exif_data);
+			
+			return data;
+		}
+		
+		
+		/// <summary>
+		///    Creates a <see cref="ByteVector"/> for the Xmp segment of this file
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the whole Xmp segment, if xmp tags
+		///    exists, otherwise null.
+		/// </returns>
+		private ByteVector RenderXMPSegment ()
+		{
+			// Check, if XmpTag is contained
+			XmpTag xmp = ImageTag.Xmp;
+			if (xmp == null)
+				return null;
+			
+			ByteVector xmp_data = XmpTag.XAP_NS + "\0";
+			xmp_data.Add (xmp.Render ());
+			
+			uint segment_size = (uint) (2 + xmp_data.Count);
+			
+			// do not render data segments, which cannot fit into the possible segment size
+			if (segment_size > ushort.MaxValue)
+				throw new Exception ("XMP Segment is too big to render");
+			
+			// Create whole segment
+			ByteVector data = new ByteVector (new byte [] { 0xFF, (byte) Marker.APP1 });
+			data.Add (ByteVector.FromUShort ((ushort) segment_size));
+			data.Add (xmp_data);
+			
+			return data;
+		}
+		
+		
+		/// <summary>
+		///    Reads a COM segment to find the JPEG comment.
+		/// </summary>
+		/// <param name="length">
+		///    The length of the segment that will be read.
+		/// </param>
+		private bool ReadCOMSegment (int length)
+		{
+			if (comment_position != 0)
+				return false;
+			
+			comment_position = Tell;
+			
+			JpegCommentTag com_tag;
+			
+			if (length == 0)
+				com_tag = new JpegCommentTag ();
+			else
+				com_tag = new JpegCommentTag (ReadBlock (length - 1).ToString ());
+			
+			ImageTag.AddTag (com_tag);
+			return true;
+		}
+		
+		/// <summary>
+		///    Creates a <see cref="ByteVector"/> for the comment segment of this file
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> with the whole comment segment, if a comment tag
+		///    exists, otherwise null.
+		/// </returns>
+		private ByteVector RenderCOMSegment ()
+		{
+			// check, if Comment is contained
+			JpegCommentTag com_tag = GetTag (TagTypes.JpegComment) as JpegCommentTag;
+			if (com_tag == null)
+				return null;
+			
+			// create comment data
+			ByteVector com_data =
+				ByteVector.FromString (com_tag.Value + "\0", StringType.Latin1);
+			
+			uint segment_size = (uint) (2 + com_data.Count);
+			
+			// do not render data segments, which cannot fit into the possible segment size
+			if (segment_size > ushort.MaxValue)
+				throw new Exception ("Comment Segment is too big to render");
+			
+			// create segment
+			ByteVector data = new ByteVector (new byte [] { 0xFF, (byte) Marker.COM });
+			data.Add (ByteVector.FromUShort ((ushort) segment_size));
+			
+			data.Add (com_data);
+			
+			return data;
+		}
+
+		/// <summary>
+		///    Reads and parse a SOF segment
+		/// </summary>
+		/// <param name="length">
+		///    The length of the segment that will be read.
+		/// </param>
+		/// <param name="marker">
+		///    The SOFx marker.
+		/// </param>
+		bool ReadSOFSegment (int length, Marker marker)
+		{
+#pragma warning disable 219 // Assigned, never read
+			byte p = ReadBlock (1)[0];	//precision
+#pragma warning restore 219
+			
+			//FIXME: according to specs, height could be 0 here, and should be retrieved from the DNL marker
+			height = ReadBlock (2).ToUShort ();
+			width = ReadBlock (2).ToUShort ();
+
+			return false;
+		}
+
+		/// <summary>
+		///    Reads the DQT Segment, and Guesstimate the image quality from it
+		/// </summary>
+		/// <param name="length">
+		///    The length of the segment that will be read
+		/// </param>
+		bool ReadDQTSegment (int length)
+		{
+			// See CCITT Rec. T.81 (1992 E), B.2.4.1 (p39) for DQT syntax
+			while (length > 0) {
+
+				byte pqtq = ReadBlock (1)[0]; length --;
+				byte pq = (byte)(pqtq >> 4);	//0 indicates 8-bit Qk, 1 indicates 16-bit Qk
+				byte tq = (byte)(pqtq & 0x0f);	//table index;
+				int [] table = null;
+				switch (tq) {
+				case 0:
+					table = Table.StandardLuminanceQuantization;
+					break;
+				case 1:
+					table = Table.StandardChrominanceQuantization;
+					break;
+				}
+
+				bool allones = true; //check for all-ones tables (q=100)
+				double cumsf = 0.0;
+				//double cumsf2 = 0.0;
+				for (int row = 0; row < 8; row ++) {
+					for (int col = 0; col < 8; col++) {
+						ushort val = ReadBlock (pq == 1 ? 2 : 1).ToUShort (); length -= (pq + 1);
+						if (table != null) {
+							double x = 100.0 * (double)val / (double)table [row*8+col]; //Scaling factor in percent
+							cumsf += x;
+							//cumsf2 += x*x;
+							allones = allones && (val == 1);
+						}
+					}
+				}
+				
+				if (table != null) {
+					double local_q;
+					cumsf /= 64.0;		// mean scale factor
+					//cumfs2 /= 64.0;
+					//double variance = cumsf2 - (cumsf * cumsf);
+
+					if (allones) 
+						local_q = 100.0;
+					else if (cumsf <= 100.0)
+						local_q = (200.0 - cumsf) / 2.0;
+					else
+						local_q = 5000.0 / cumsf;
+					quality = Math.Max (quality, (int)local_q);
+				}
+			}
+			return false;
+		}
+
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Jpeg/JpegCommentTag.cs b/lib/TagLib/TagLib/Jpeg/JpegCommentTag.cs
new file mode 100644
index 0000000..5421d43
--- /dev/null
+++ b/lib/TagLib/TagLib/Jpeg/JpegCommentTag.cs
@@ -0,0 +1,101 @@
+//
+// JpegCommentTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+
+using TagLib.Image;
+
+namespace TagLib.Jpeg
+{
+	/// <summary>
+	///    Contains the JPEG comment.
+	/// </summary>
+	public class JpegCommentTag : ImageTag
+	{
+#region Constructors
+
+		/// <summary>
+		///    Constructor.
+		/// </summary>
+		/// <param name="value">
+		///    The value of the comment.
+		/// </param>
+		public JpegCommentTag (string value)
+		{
+			Value = value;
+		}
+		
+		/// <summary>
+		///    Constructor. Creates a new empty comment.
+		/// </summary>
+		public JpegCommentTag () {
+			Value = null;
+		}
+
+#endregion
+
+#region Public Properties
+		
+		/// <summary>
+		///    The value of the comment represented by the current instance.
+		/// </summary>
+		public string Value { get; set; }
+		
+		/// <summary>
+		///    Gets or sets the comment for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the comment of the
+		///    current instace.
+		/// </value>
+		public override string Comment {
+			get { return Value; }
+			set { Value = value; }
+		}
+		
+#endregion
+
+#region Public Methods
+
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.JpegComment" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get { return TagTypes.JpegComment; }
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			Value = null;
+		}
+
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Jpeg/Marker.cs b/lib/TagLib/TagLib/Jpeg/Marker.cs
new file mode 100644
index 0000000..3c1bed5
--- /dev/null
+++ b/lib/TagLib/TagLib/Jpeg/Marker.cs
@@ -0,0 +1,349 @@
+//
+// Marker.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//   Stephane Delcroix (stephane delcroix org)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+// Copyright (c) 2009 Stephane Delcroix
+//
+// 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
+//
+
+namespace TagLib.Jpeg
+{
+	/// <summary>
+	///    This enum defines the different markers used in JPEG segments.
+	///
+	///    See CCITT Rec. T.81 (1992 E), Table B.1 (p.32)
+	/// </summary>
+	public enum Marker : byte {
+		/// <summary>
+		///    Start Of Frame marker, non-differential, Huffman coding, Baseline DCT
+		/// </summary>
+		SOF0 = 0xc0,
+
+		/// <summary>
+		///    Start Of Frame marker, non-differential, Huffman coding, Extended Sequential DCT
+		/// </summary>
+		SOF1,
+
+		/// <summary>
+		///    Start Of Frame marker, non-differential, Huffman coding, Progressive DCT
+		/// </summary>
+		SOF2,
+
+		/// <summary>
+		///    Start Of Frame marker, non-differential, Huffman coding, Lossless (sequential)
+		/// </summary>
+		SOF3,
+
+		/// <summary>
+		///    Start Of Frame marker, differential, Huffman coding, Differential Sequential DCT
+		/// </summary>
+		SOF5 = 0xc5,
+
+		/// <summary>
+		///    Start Of Frame marker, differential, Huffman coding, Differential Progressive DCT
+		/// </summary>
+		SOF6,
+		/// <summary>
+		///    Start Of Frame marker, differential, Huffman coding, Differential Lossless (sequential)
+		/// </summary>
+		SOF7,
+
+		/// <summary>
+		///    Reserved for JPG extensions
+		/// </summary>
+		JPG,
+
+		/// <summary>
+		///    Start Of Frame marker, non-differential, arithmetic coding, Extended Sequential DCT
+		/// </summary>
+		SOF9,
+
+		/// <summary>
+		///    Start Of Frame marker, non-differential, arithmetic coding, Progressive DCT
+		/// </summary>
+		SOF10,
+
+		/// <summary>
+		///    Start Of Frame marker, non-differential, arithmetic coding, Lossless (sequential)
+		/// </summary>
+		SOF11,
+
+		/// <summary>
+		///    Start Of Frame marker, differential, arithmetic coding, Differential Sequential DCT
+		/// </summary>
+		SOF13 = 0xcd,
+
+		/// <summary>
+		///    Start Of Frame marker, differential, arithmetic coding, Differential Progressive DCT
+		/// </summary>
+		SOF14,
+
+		/// <summary>
+		///    Start Of Frame marker, differential, arithmetic coding, Differential Lossless (sequential)
+		/// </summary>
+		SOF15,
+
+		/// <summary>
+		///    Define Huffman table(s)
+		/// </summary>
+		DHT = 0xc4,
+		
+		/// <summary>
+		///    Define arithmetic coding conditioning(s)
+		/// </summary>
+		DAC = 0xcc,
+
+		//Restart interval termination with modulo 8 count "m"
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST0 = 0xd0,
+
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST1,
+
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST2,
+
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST3,
+
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST4,
+
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST5,
+
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST6,
+
+		/// <summary>
+		///    Restart
+		/// </summary>
+		RST7,
+
+		/// <summary>
+		///    Start of Image
+		/// </summary>
+		SOI = 0xd8,
+
+		/// <summary>
+		///    End of Image
+		/// </summary>
+		EOI,
+
+		/// <summary>
+		///    Start of scan
+		/// </summary>
+		SOS,
+
+		/// <summary>
+		///    Define quantization table (s)
+		/// </summary>
+		DQT,
+
+		/// <summary>
+		///    Define number of lines
+		/// </summary>
+		DNL,
+
+		/// <summary>
+		///    Define restart interval
+		/// </summary>
+		DRI,
+
+		/// <summary>
+		///    Define hierarchical progression
+		/// </summary>
+		DHP,
+
+		/// <summary>
+		///    Define reference component
+		/// </summary>
+		EXP,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP0 = 0xe0,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP1,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP2,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP3,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP4,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP5,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP6,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP7,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP8,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP9,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP10,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP11,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP12,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP13,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP14,
+
+		/// <summary>
+		///    Reserved for application segment
+		/// </summary>
+		APP15,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG0 = 0xf0,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG1,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG2,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG3,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG4,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG5,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG6,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG7,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG8,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG9,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG10,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG11,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG12,
+
+		/// <summary>
+		///    Reserved for JPEG extension
+		/// </summary>
+		JPG13,
+
+		/// <summary>
+		///   Comment 
+		/// </summary>
+		COM = 0xfe,
+	}
+}
diff --git a/lib/TagLib/TagLib/Jpeg/Table.cs b/lib/TagLib/TagLib/Jpeg/Table.cs
new file mode 100644
index 0000000..cc5bf55
--- /dev/null
+++ b/lib/TagLib/TagLib/Jpeg/Table.cs
@@ -0,0 +1,64 @@
+//
+// TagLib.Jpeg.Table.cs:
+//
+// Author:
+//   Stephane Delcroix (stephane delcroix org)
+//
+// Copyright (c) 2009 Stephane Delcroix
+//
+// 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
+//
+
+namespace TagLib.Jpeg
+{
+	/// <summary>
+	///    Contains static predefined tables and helpers
+	/// </summary>
+	public static class Table
+	{
+		/// <summary>
+		///    Standard Luminance Quantization table
+		///
+		///    See CCIT Rec. T.81 (1992 E), K.1 (p143)
+		/// </summary>
+		public static int [] StandardLuminanceQuantization = new int [] {
+			16,  11,  12,  14,  12,  10,  16,  14,
+			13,  14,  18,  17,  16,  19,  24,  40,
+			26,  24,  22,  22,  24,  49,  35,  37,
+			29,  40,  58,  51,  61,  60,  57,  51,
+			56,  55,  64,  72,  92,  78,  64,  68,
+			87,  69,  55,  56,  80, 109,  81,  87,
+			95,  98, 103, 104, 103,  62,  77, 113,
+			121, 112, 100, 120,  92, 101, 103, 99
+		};              
+                        
+		/// <summary>
+		///    Standard Chrominance Quantization table
+		///
+		///    See CCIT Rec. T.81 (1992 E), K.1 (p143)
+		/// </summary>
+		public static int [] StandardChrominanceQuantization = new int [] {
+			17,  18,  18,  24,  21,  24,  47,  26,
+			26,  47,  99,  66,  56,  66,  99,  99,
+			99,  99,  99,  99,  99,  99,  99,  99,
+			99,  99,  99,  99,  99,  99,  99,  99,
+			99,  99,  99,  99,  99,  99,  99,  99,
+			99,  99,  99,  99,  99,  99,  99,  99,
+			99,  99,  99,  99,  99,  99,  99,  99,
+			99,  99,  99,  99,  99,  99,  99,  99
+		};                      
+	}
+}
+
diff --git a/lib/TagLib/TagLib/ListBase.cs b/lib/TagLib/TagLib/ListBase.cs
new file mode 100644
index 0000000..cf70b53
--- /dev/null
+++ b/lib/TagLib/TagLib/ListBase.cs
@@ -0,0 +1,459 @@
+//
+// ListBase.cs:
+//
+// Author:
+//   Aaron Bockover (abockover novell com)
+//
+// Original Source:
+//   tbytevectorlist.cpp from TagLib
+//
+// Copyright (C) 2006 Novell, Inc.
+// Copyright (C) 2002,2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Text;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace TagLib {
+	/// <summary>
+	///    This class implements <see cref="T:System.Collections.Generic`1"/>
+	///    for objects that implement <see cref="T:System.IComparable`1"/>,
+	///    providing extra features used in lists in TagLib#.
+	/// </summary>
+	public class ListBase<T> : IList<T> where T : IComparable<T>
+	{
+		/// <summary>
+		///    Contains the internal list.
+		/// </summary>
+		private List<T> data = new List<T> ();
+
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="T:TagLib.ListBase`1" /> with no contents.
+		/// </summary>
+		public ListBase ()
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="T:TagLib.ListBase`1" /> with specified contents.
+		/// </summary>
+		/// <param name="list">
+		///   A <see cref="T:System.Collections.Generic.IEnumerable`1"
+		///   /> containing objects to add to the current instance.
+		/// </param>
+		public ListBase(ListBase<T> list)
+		{
+			if (list != null)
+				Add (list);
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="T:TagLib.ListBase`1" /> with specified contents.
+		/// </summary>
+		/// <param name="list">
+		///   A <see cref="System.Array" /> containing objects to add to
+		///   the current instance.
+		/// </param>
+		public ListBase (params T [] list)
+		{
+			if (list != null)
+				Add (list);
+		}
+
+		#endregion
+
+		#region Properties
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance is empty;
+		///    otherwise <see langword="false" />.
+		/// </value>
+		public bool IsEmpty {
+			get {return Count == 0;}
+		}
+
+		#endregion
+
+		#region Methods
+
+		/// <summary>
+		///    Adds a collection of elements to the current instance.
+		/// </summary>
+		/// <param name="list">
+		///    A <see cref="T:TagLib.ListBase`1"/> object containing
+		///    elements to add to the current instance.
+		/// </param>
+		public void Add(ListBase<T> list)
+		{
+			if(list != null) {
+				data.AddRange(list);
+			}
+		}
+
+		/// <summary>
+		///    Adds a collection of elements to the current instance.
+		/// </summary>
+		/// <param name="list">
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1"/> object containing
+		///    elements to add to the current instance.
+		/// </param>
+		public void Add(IEnumerable<T> list)
+		{
+			if(list != null) {
+				data.AddRange(list);
+			}
+		}
+
+		/// <summary>
+		///    Adds a collection of elements to the current instance.
+		/// </summary>
+		/// <param name="list">
+		///    An array containing elements to add to the current
+		///    instance.
+		/// </param>
+		public void Add(T [] list)
+		{
+			if(list != null) {
+				data.AddRange(list);
+			}
+		}
+
+		/// <summary>
+		///    Performs a sorted insert of an object into the current
+		///    instance, optionally only adding if the item is unique.
+		/// </summary>
+		/// <param name="item">
+		///    An object to add to the current instance.
+		/// </param>
+		/// <param name="unique">
+		///    If <see langword="true" />, the object will only be added
+		///    if an identical value is not already contained in the
+		///    current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="item" /> is <see langword="null" />.
+		/// </exception>
+		public virtual void SortedInsert (T item, bool unique)
+		{
+			if (item == null)
+				throw new ArgumentNullException ("item");
+			
+			int i = 0;
+			for(; i < data.Count; i++) {
+				if(item.CompareTo(data[i]) == 0 && unique) {
+					return;
+				}
+
+				if(item.CompareTo(data[i]) <= 0) {
+					break;
+				}
+			}
+
+			Insert(i, item);
+		}
+
+		/// <summary>
+		///    Performs a sorted insert of an object into the current
+		///    instance.
+		/// </summary>
+		/// <param name="item">
+		///    An object to add to the current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="item" /> is <see langword="null" />.
+		/// </exception>
+		public void SortedInsert (T item)
+		{
+			if (item == null)
+				throw new ArgumentNullException ("item");
+			
+			SortedInsert(item, false);
+		}
+		
+		/// <summary>
+		///    Converts the current instance to an array.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.Array" /> containing the contents of
+		///    the current instance.
+		/// </returns>
+		public T [] ToArray ()
+		{
+			return data.ToArray();
+		}
+
+		#endregion
+
+#region IList<T>
+		
+		/// <summary>
+		///    Gets whether or not the current instance is read-only.
+		/// </summary>
+		/// <value>
+		///    Always <see langword="false" />.
+		/// </value>
+		public bool IsReadOnly {
+			get { return false; }
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance has a fixed
+		///    size.
+		/// </summary>
+		/// <value>
+		///    Always <see langword="false" />.
+		/// </value>
+		public bool IsFixedSize {
+			get { return false; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the value as a specified index.
+		/// </summary>
+		public T this [int index] {
+			get { return data[index]; }
+			set { data[index] = value; }
+		}
+		
+		/// <summary>
+		///    Adds a single item to end of the current instance.
+		/// </summary>
+		/// <param name="item">
+		///    An object to add to the end of the current instance.
+		/// </param>
+		public void Add (T item)
+		{
+			data.Add (item);
+		}
+		
+		/// <summary>
+		///    Clears the contents of the current instance.
+		/// </summary>
+		public void Clear ()
+		{
+			data.Clear ();
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance contains a
+		///    specified object.
+		/// </summary>
+		/// <param name="item">
+		///    An object to look for in the current instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the item could be found;
+		///    otherwise <see langword="false" />.
+		/// </returns>
+		public bool Contains (T item)
+		{
+			return data.Contains (item);
+		}
+		
+		/// <summary>
+		///    Gets the index of the first occurance of a value.
+		/// </summary>
+		/// <param name="item">
+		///    A object to find in the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value containing the first index
+		///    at which the value was found, or -1 if it was not found.
+		/// </returns>
+		public int IndexOf (T item)
+		{
+			return data.IndexOf (item);
+		}
+		
+		/// <summary>
+		///    Inserts a single value into the current instance at a
+		//     specified index.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the position at
+		///    which to insert the value.
+		/// </param>
+		/// <param name="item">
+		///    An object to insert into the current instance.
+		/// </param>
+		public void Insert (int index, T item)
+		{
+			data.Insert (index, item);
+		}
+		
+		/// <summary>
+		///    Removes the first occurance of an object from the current
+		///    instance.
+		/// </summary>
+		/// <param name="item">
+		///    An object to remove from the current instance.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the value was removed;
+		///    otherwise the value did not appear in the current
+		///    instance and <see langword="false" /> is returned.
+		/// </returns>
+		public bool Remove (T item)
+		{
+			return data.Remove (item);
+		}
+		
+		/// <summary>
+		///    Removes the item at the specified index.
+		/// </summary>
+		/// <param name="index">
+		///    A <see cref="int" /> value specifying the position at
+		///    which to remove an item.
+		/// </param>
+		public void RemoveAt (int index)
+		{
+			data.RemoveAt (index);
+		}
+		
+		/// <summary>
+		///    Gets a string representation of the contents of the
+		///    current instance, joined by a separator.
+		/// </summary>
+		/// <param name="separator">
+		///    A <see cref="string" /> object to separate the items
+		///    with.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string" /> object containing the contents
+		///    of the current instance.
+		/// </returns>
+		public string ToString (string separator)
+		{
+			StringBuilder builder = new StringBuilder();
+
+			for(int i = 0; i < Count; i++) {
+				if(i != 0) {
+					builder.Append(separator);
+				}
+
+				builder.Append(this[i].ToString());
+			}
+
+			return builder.ToString ();
+		}
+
+		/// <summary>
+		///    Gets a string representation of the contents of the
+		///    current instance, joined by commas.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="string" /> object containing the contents
+		///    of the current instance.
+		/// </returns>
+		public override string ToString ()
+		{
+			return ToString(", ");
+		}
+
+#endregion
+		
+		
+		
+#region ICollection<T>
+		
+		/// <summary>
+		///    Gets the number of elements in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    elements in the current instance.
+		/// </value>
+		public int Count {
+			get {return data.Count;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is synchronized.
+		/// </summary>
+		/// <value>
+		///    Always <see langword="false" />.
+		/// </value>
+		public bool IsSynchronized { 
+			get {return false;}
+		}
+		
+		/// <summary>
+		///    Gets the object that can be used to synchronize the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="object" /> that can be used to synchronize
+		///    the current instance.
+		/// </value>
+		public object SyncRoot { 
+			get {return this;}
+		}
+		
+		/// <summary>
+		///    Copies the current instance to an array, starting at a
+		///    specified index.
+		/// </summary>
+		/// <param name="array">
+		///    An array to copy to.
+		/// </param>
+		/// <param name="arrayIndex">
+		///    A <see cref="int" /> value indicating the index in
+		///    <paramref name="array" /> at which to start copying.
+		/// </param>
+		public void CopyTo (T [] array, int arrayIndex)
+		{
+			data.CopyTo (array, arrayIndex);
+		}
+		
+#endregion
+		
+		
+		
+		
+#region IEnumerable<T>
+		
+		/// <summary>
+		///    Gets an enumerator for enumerating through the elements
+		///    in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the tag's data boxes.
+		/// </returns>
+		public IEnumerator<T> GetEnumerator()
+		{
+			return data.GetEnumerator();
+		}
+		
+		IEnumerator IEnumerable.GetEnumerator()
+		{
+			return data.GetEnumerator();
+		}
+
+#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Mpc/File.cs b/lib/TagLib/TagLib/Mpc/File.cs
new file mode 100644
index 0000000..61a29f3
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpc/File.cs
@@ -0,0 +1,278 @@
+//
+// File.cs: Provides tagging and properties support for MusePack files.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   mpcfile.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2004 by Allan Sandfeld Jensen (Original 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
+//
+
+using System;
+
+namespace TagLib.MusePack {
+	/// <summary>
+	///    This class extends <see cref="TagLib.NonContainer.File" /> to
+	///    provide tagging and properties support for MusePack files.
+	/// </summary>
+	/// <remarks>
+	///    A <see cref="TagLib.Ape.Tag" /> will be added automatically to
+	///    any file that doesn't contain one. This change does not effect
+	///    the file and can be reversed using the following method:
+	///    <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
+	/// </remarks>
+	[SupportedMimeType("taglib/mpc", "mpc")]
+	[SupportedMimeType("taglib/mp+", "mp+")]
+	[SupportedMimeType("taglib/mpp", "mpp")]
+	[SupportedMimeType("audio/x-musepack")]
+	public class File : TagLib.NonContainer.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the block with the audio header.
+		/// </summary>
+		private ByteVector header_block = null;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle)
+			: base (abstraction, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: base (abstraction)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    If a <see cref="TagLib.Id3v2.Tag" /> is added to the
+		///    current instance, it will be placed at the start of the
+		///    file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
+		///    <see cref="TagLib.Ape.Tag" /> will be added to the end of
+		///    the file. All other tag types will be ignored.
+		/// </remarks>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
+			
+			if (t != null || !create)
+				return t;
+			
+			switch (type)
+			{
+			case TagTypes.Id3v1:
+				return EndTag.AddTag (type, Tag);
+			
+			case TagTypes.Id3v2:
+				return StartTag.AddTag (type, Tag);
+			
+			case TagTypes.Ape:
+				return EndTag.AddTag (type, Tag);
+			
+			default:
+				return null;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadStart (long start,
+		                                   ReadStyle propertiesStyle)
+		{
+			if (header_block != null &&
+				propertiesStyle == ReadStyle.None)
+				return;
+				
+			Seek (start);
+			header_block = ReadBlock (
+				(int) StreamHeader.Size);
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadEnd (long end,
+		                                 ReadStyle propertiesStyle)
+		{
+			// Make sure we have an APE tag.
+			GetTag (TagTypes.Ape, true);
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		protected override Properties ReadProperties (long start,
+		                                              long end,
+		                                              ReadStyle propertiesStyle)
+		{
+			StreamHeader header = new StreamHeader (header_block,
+				end - start);
+			return new Properties (TimeSpan.Zero, header);
+		}
+		
+		#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Mpc/StreamHeader.cs b/lib/TagLib/TagLib/Mpc/StreamHeader.cs
new file mode 100644
index 0000000..8895cdb
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpc/StreamHeader.cs
@@ -0,0 +1,363 @@
+//
+// StreamHeader.cs: Provides support for reading MusePack audio properties.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   mpcproperties.cpp from TagLib
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// Copyright (C) 2004 by Allan Sandfeld Jensen (Original 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
+//
+
+using System;
+
+namespace TagLib.MusePack {
+	/// <summary>
+	///    This struct implements <see cref="IAudioCodec" /> to provide
+	///    support for reading MusePack audio properties.
+	/// </summary>
+	public struct StreamHeader : IAudioCodec
+	{
+		#region Constants
+		
+		private static ushort [] sftable = {44100, 48000, 37800, 32000};
+		
+		#endregion
+		
+		
+		
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the number of bytes in the stream.
+		/// </summary>
+		private long stream_length;
+		
+		/// <summary>
+		///    Contains the MusePack version.
+		/// </summary>
+		private int version;
+		
+		/// <summary>
+		///    Contains additional header information.
+		/// </summary>
+		private uint header_data;
+		
+		/// <summary>
+		///    Contains the sample rate of the stream.
+		/// </summary>
+		private int sample_rate;
+		
+		/// <summary>
+		///    Contains the number of frames in the stream.
+		/// </summary>
+		private uint frames;
+		
+		#endregion
+		
+		
+		
+		#region Public Static Fields
+		
+		/// <summary>
+		///    The size of a MusePack header.
+		/// </summary>
+		public const uint Size = 56;
+		
+		/// <summary>
+		///    The identifier used to recognize a WavPack file.
+		/// </summary>
+		/// <value>
+		///    "MP+"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "MP+";
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StreamHeader" /> for a specified header block and
+		///    stream length.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the stream
+		///    header data.
+		/// </param>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value containing the length of the
+		///    MusePAck stream in bytes.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> does not begin with <see
+		///    cref="FileIdentifier" /> or is less than <see cref="Size"
+		///    /> bytes long.
+		/// </exception>
+		public StreamHeader (ByteVector data, long streamLength)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"Data does not begin with identifier.");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"Insufficient data in stream header");
+			
+			stream_length = streamLength;
+			version = data [3] & 15;
+			
+			if (version >= 7) {
+				frames = data.Mid (4, 4).ToUInt (false);
+				uint flags = data.Mid (8, 4).ToUInt (false);
+				sample_rate = sftable [(int) (((flags >> 17) &
+					1) * 2 + ((flags >> 16) & 1))];
+				header_data = 0;
+			} else {
+				header_data = data.Mid (0, 4).ToUInt (false);
+				version = (int) ((header_data >> 11) & 0x03ff);
+				sample_rate = 44100;
+				frames = data.Mid (4,
+					version >= 5 ? 4 : 2).ToUInt (false);
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		
+		public TimeSpan Duration {
+			get {
+				if (sample_rate <= 0 && stream_length <= 0)
+					return TimeSpan.Zero;
+				
+				return TimeSpan.FromSeconds (
+					(double) (frames * 1152 - 576) /
+					(double) sample_rate + 0.5);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {return string.Format (
+				System.Globalization.CultureInfo.InvariantCulture,
+				"MusePack Version {0} Audio", Version);}
+		}
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				if (header_data != 0)
+					return (int) ((header_data >> 23) & 0x01ff);
+				
+				return (int) (Duration > TimeSpan.Zero ?
+					((stream_length * 8L) /
+					Duration.TotalSeconds) / 1000 : 0);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {return sample_rate;}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return 2;}
+		}
+		
+		/// <summary>
+		///    Gets the WavPack version of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the WavPack version
+		///    of the audio represented by the current instance.
+		/// </value>
+		public int Version {
+			get {return version;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region IEquatable
+		
+		/// <summary>
+		///    Generates a hash code for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value containing the hash code for
+		///    the current instance.
+		/// </returns>
+		public override int GetHashCode ()
+		{
+			unchecked {
+				return (int) (header_data ^ sample_rate ^
+					frames ^ version);
+			}
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another object.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="object" /> to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public override bool Equals (object other)
+		{
+			if (!(other is StreamHeader))
+				return false;
+			
+			return Equals ((StreamHeader) other);
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another instance of <see cref="StreamHeader" />.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="StreamHeader" /> object to compare to the
+		///    current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public bool Equals (StreamHeader other)
+		{
+			return header_data == other.header_data &&
+				sample_rate == other.sample_rate &&
+				version == other.version &&
+				frames == other.frames;
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="StreamHeader" /> are equal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    equal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator == (StreamHeader first,
+		                                StreamHeader second)
+		{
+			return first.Equals (second);
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="StreamHeader" /> differ.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    unequal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator != (StreamHeader first,
+		                                StreamHeader second)
+		{
+			return !first.Equals (second);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg/AudioFile.cs b/lib/TagLib/TagLib/Mpeg/AudioFile.cs
new file mode 100644
index 0000000..38ad386
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg/AudioFile.cs
@@ -0,0 +1,296 @@
+//
+// AudioFile.cs: Provides tagging and properties support for MPEG-1, MPEG-2, and
+// MPEG-2.5 audio files.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   mpegfile.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2002, 2003 by Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Mpeg {
+	/// <summary>
+	///    This class extends <see cref="TagLib.NonContainer.File" /> to
+	///    provide tagging and properties support for MPEG-1, MPEG-2, and
+	///    MPEG-2.5 audio files.
+	/// </summary>
+	/// <remarks>
+	///    A <see cref="TagLib.Id3v1.Tag" /> and <see
+	///    cref="TagLib.Id3v2.Tag" /> will be added automatically to any
+	///    file that doesn't contain one. This change does not effect the
+	///    file until it is saved and can be reversed using the following
+	///    method:
+	///    <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
+	/// </remarks>
+	[SupportedMimeType("taglib/mp3", "mp3")]
+	[SupportedMimeType("audio/x-mp3")]
+	[SupportedMimeType("application/x-id3")]
+	[SupportedMimeType("audio/mpeg")]
+	[SupportedMimeType("audio/x-mpeg")]
+	[SupportedMimeType("audio/x-mpeg-3")]
+	[SupportedMimeType("audio/mpeg3")]
+	[SupportedMimeType("audio/mp3")]
+	[SupportedMimeType("taglib/m2a", "m2a")]
+	[SupportedMimeType("taglib/mp2", "mp2")]
+	[SupportedMimeType("taglib/mp1", "mp1")]
+	[SupportedMimeType("audio/x-mp2")]
+	[SupportedMimeType("audio/x-mp1")]
+	public class AudioFile : TagLib.NonContainer.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the first audio header.
+		/// </summary>
+		private AudioHeader first_header;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AudioFile" /> for a specified path in the local
+		///    file system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public AudioFile (string path, ReadStyle propertiesStyle)
+			: base (path, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AudioFile" /> for a specified path in the local
+		///    file system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public AudioFile (string path) : base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AudioFile" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="TagLib.File.IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public AudioFile (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle)
+			: base (abstraction, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AudioFile" /> for a specified file abstraction with
+		///    an average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="TagLib.File.IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public AudioFile (File.IFileAbstraction abstraction)
+			: base (abstraction)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    If a <see cref="TagLib.Id3v2.Tag" /> is added to the
+		///    current instance, it will be placed at the start of the
+		///    file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
+		///    <see cref="TagLib.Ape.Tag" /> will be added to the end of
+		///    the file. All other tag types will be ignored.
+		/// </remarks>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
+			
+			if (t != null || !create)
+				return t;
+			
+			switch (type)
+			{
+			case TagTypes.Id3v1:
+				return EndTag.AddTag (type, Tag);
+			
+			case TagTypes.Id3v2:
+				return StartTag.AddTag (type, Tag);
+			
+			case TagTypes.Ape:
+				return EndTag.AddTag (type, Tag);
+			
+			default:
+				return null;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <remarks>
+		///    This method only searches for an audio header in the
+		///    first 16384 bytes of code to avoid searching forever in
+		///    corrupt files.
+		/// </remarks>
+		protected override void ReadStart (long start,
+		                                   ReadStyle propertiesStyle)
+		{
+			// Only check the first 16 bytes so we're not stuck
+			// reading a bad file forever.
+			if (propertiesStyle != ReadStyle.None &&
+				!AudioHeader.Find (out first_header, this,
+					start, 0x4000))
+				throw new CorruptFileException (
+					"MPEG audio header not found.");
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadEnd (long end,
+		                                 ReadStyle propertiesStyle)
+		{
+			// Make sure we have ID3v1 and ID3v2 tags.
+			GetTag (TagTypes.Id3v1, true);
+			GetTag (TagTypes.Id3v2, true);
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		protected override Properties ReadProperties (long start,
+		                                              long end,
+		                                              ReadStyle propertiesStyle)
+		{
+			first_header.SetStreamLength (end - start);
+			return new Properties (TimeSpan.Zero, first_header);
+		}
+		
+		#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Mpeg/AudioHeader.cs b/lib/TagLib/TagLib/Mpeg/AudioHeader.cs
new file mode 100644
index 0000000..adf8575
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg/AudioHeader.cs
@@ -0,0 +1,785 @@
+//
+// AudioHeader.cs: Provides information about an MPEG audio stream.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   mpegheader.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2003 by Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib.Mpeg {
+	
+	#region Enums
+	
+	/// <summary>
+	///    Indicates the MPEG version of a file or stream.
+	/// </summary>
+	public enum Version
+	{
+		/// <summary>
+		///    Unknown version.
+		/// </summary>
+		Unknown = -1,
+		
+		/// <summary>
+		///    MPEG-1
+		/// </summary>
+		Version1 = 0,
+		
+		/// <summary>
+		///    MPEG-2
+		/// </summary>
+		Version2 = 1,
+		
+		/// <summary>
+		///    MPEG-2.5
+		/// </summary>
+		Version25 = 2
+	}
+	
+	/// <summary>
+	///    Indicates the MPEG audio channel mode of a file or stream.
+	/// </summary>
+	public enum ChannelMode
+	{
+		/// <summary>
+		///    Stereo
+		/// </summary>
+		Stereo = 0,
+		
+		/// <summary>
+		///    Joint Stereo
+		/// </summary>
+		JointStereo = 1,
+		
+		/// <summary>
+		///    Dual Channel Mono
+		/// </summary>
+		DualChannel = 2,
+		
+		/// <summary>
+		///    Single Channel Mono
+		/// </summary>
+		SingleChannel = 3
+	}
+	
+	#endregion
+	
+	/// <summary>
+	///    This structure implements <see cref="IAudioCodec" /> and provides
+	///    information about an MPEG audio stream.
+	/// </summary>
+	public struct AudioHeader : IAudioCodec
+	{
+		#region Private Static Value Arrays
+		
+		/// <summary>
+		///    Contains a sample rate table for MPEG audio.
+		/// </summary>
+		private static readonly int [,] sample_rates = new int [3,4] {
+			{44100, 48000, 32000, 0}, // Version 1
+			{22050, 24000, 16000, 0}, // Version 2
+			{11025, 12000,  8000, 0}  // Version 2.5
+		};
+		
+		/// <summary>
+		///    Contains a block size table for MPEG audio.
+		/// </summary>
+		private static readonly int [,] block_size = new int [3,4] {
+			{0, 384, 1152, 1152}, // Version 1
+			{0, 384, 1152,  576}, // Version 2
+			{0, 384, 1152,  576}  // Version 2.5
+		};
+		
+		/// <summary>
+		///    Contains a bitrate table for MPEG audio.
+		/// </summary>
+		private static readonly int [,,] bitrates = new int [2,3,16] {
+			{ // Version 1
+				{0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
+					320, 352, 384, 416, 448, -1}, // layer 1
+				{0, 32, 48, 56,  64,  80,  96, 112, 128, 160,
+					192, 224, 256, 320, 384, -1}, // layer 2
+				{0, 32, 40, 48,  56,  64,  80,  96, 112, 128,
+					160, 192, 224, 256, 320, -1}  // layer 3
+			},
+			{ // Version 2 or 2.5
+				{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160,
+					176, 192, 224, 256, -1}, // layer 1
+				{0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96,
+					112, 128, 144, 160, -1}, // layer 2
+				{0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96,
+					112, 128, 144, 160, -1}  // layer 3
+			}
+		};
+		
+		#endregion
+		
+		
+		
+		#region Private Properties
+		
+		/// <summary>
+		///    Contains the header flags.
+		/// </summary>
+		private uint flags;
+		
+		/// <summary>
+		///    Contains the audio stream length.
+		/// </summary>
+		private long stream_length;
+		
+		/// <summary>
+		///    Contains the associated Xing header.
+		/// </summary>
+		private XingHeader xing_header;
+		
+		/// <summary>
+		///    Contains the associated VBRI header.
+		/// </summary>
+		private VBRIHeader vbri_header;
+		
+		/// <summary>
+		///    Contains the audio stream duration.
+		/// </summary>
+		private TimeSpan duration;
+		
+		#endregion
+		
+		
+		
+		#region Public Fields
+		
+		/// <summary>
+		///    An empty and unset header.
+		/// </summary>
+		public static readonly AudioHeader Unknown =
+			new AudioHeader (0, 0, XingHeader.Unknown,
+				VBRIHeader.Unknown);
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AudioHeader" /> by populating it with specified
+		///    values.
+		/// </summary>
+		/// <param name="flags">
+		///    A <see cref="uint" /> value specifying flags for the new
+		///    instance.
+		/// </param>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value specifying the stream length
+		///    of the new instance.
+		/// </param>
+		/// <param name="xingHeader">
+		///    A <see cref="XingHeader" /> object representing the Xing
+		///    header associated with the new instance.
+		/// </param>
+		/// <param name="vbriHeader">
+		///    A <see cref="VBRIHeader" /> object representing the VBRI
+		///    header associated with the new instance.
+		/// </param>
+		private AudioHeader (uint flags, long streamLength,
+		                     XingHeader xingHeader,
+		                     VBRIHeader vbriHeader)
+		{
+			this.flags = flags;
+			this.stream_length = streamLength;
+			this.xing_header = xingHeader;
+			this.vbri_header = vbriHeader;
+			this.duration = TimeSpan.Zero;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AudioHeader" /> by reading its contents from a
+		///    <see cref="ByteVector" /> object and its Xing Header from
+		///    the appropriate location in the specified file.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the header
+		///    to read.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the Xing
+		///    header from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value indicating the position in
+		///    <paramref name="file" /> at which the header begins.
+		/// </param>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> is less than 4 bytes long,
+		///    does not begin with a MPEG audio synch, has a negative
+		///    bitrate, or has a sample rate of zero.
+		/// </exception>
+		private AudioHeader (ByteVector data, TagLib.File file,
+		                     long position)
+		{
+			this.duration = TimeSpan.Zero;
+			stream_length = 0;
+			
+			if (data.Count < 4)
+				throw new CorruptFileException (
+					"Insufficient header length.");
+			
+			if (data [0] != 0xFF)
+				throw new CorruptFileException (
+					"First byte did not match MPEG synch.");
+			
+			// Checking bits from high to low:
+			//
+			// First 3 bits MUST be set. Bits 4 and 5 can
+			// be 00, 10, or 11 but not 01. One or more of
+			// bits 6 and 7 must be set. Bit 8 can be
+			// anything.
+			if ((data [1] & 0xE6) <= 0xE0 || (data [1] & 0x18) == 0x08)
+				throw new CorruptFileException (
+					"Second byte did not match MPEG synch.");
+			
+			flags = data.ToUInt ();
+			
+			if (((flags >> 12) & 0x0F) == 0x0F)
+				throw new CorruptFileException (
+					"Header uses invalid bitrate index.");
+			
+			if (((flags >> 10) & 0x03) == 0x03)
+				throw new CorruptFileException (
+					"Invalid sample rate.");
+
+			xing_header = XingHeader.Unknown;
+			
+			vbri_header = VBRIHeader.Unknown;
+			
+			// Check for a Xing header that will help us in
+			// gathering information about a VBR stream.
+			file.Seek (position + XingHeader.XingHeaderOffset (
+				Version, ChannelMode));
+				
+			ByteVector xing_data = file.ReadBlock (16);
+			if (xing_data.Count == 16 && xing_data.StartsWith (
+				XingHeader.FileIdentifier))
+				xing_header = new XingHeader (xing_data);
+
+			if (xing_header.Present)
+				return;
+			
+			// A Xing header could not be found, next chec for a
+			// Fraunhofer VBRI header.
+			file.Seek (position + VBRIHeader.VBRIHeaderOffset ());
+
+			// Only get the first 24 bytes of the Header.
+			// We're not interested in the TOC entries.
+			ByteVector vbri_data = file.ReadBlock (24);
+			if (vbri_data.Count == 24 &&
+				vbri_data.StartsWith(VBRIHeader.FileIdentifier))
+			vbri_header = new VBRIHeader (vbri_data);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the MPEG version used to encode the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="Version" /> value indicating the MPEG
+		///    version used to encode the audio represented by the
+		///    current instance.
+		/// </value>
+		public Version Version {
+			get {
+				switch ((flags >> 19) & 0x03)
+				{
+				case 0:
+					return Version.Version25;
+				case 2:
+					return Version.Version2;
+				default:
+					return Version.Version1;
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets the MPEG audio layer used to encode the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value indicating the MPEG audio
+		///    layer used to encode the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioLayer {
+			get {
+				switch ((flags >> 17) & 0x03)
+				{
+				case 1:
+					return 3;
+				case 2:
+					return 2;
+				default:
+					return 1;
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				if (xing_header.TotalSize > 0 &&
+					duration > TimeSpan.Zero)
+					return (int) Math.Round (((
+						(XingHeader.TotalSize * 8L) /
+						duration.TotalSeconds) / 1000.0));
+
+				if (vbri_header.TotalSize > 0 && 
+					duration > TimeSpan.Zero)
+					return (int)Math.Round(((
+						(VBRIHeader.TotalSize * 8L) /
+						duration.TotalSeconds) / 1000.0));
+				
+				return bitrates [
+					Version == Version.Version1 ? 0 : 1,
+					AudioLayer > 0 ? AudioLayer - 1 : 0,
+					(int) (flags >> 12) & 0x0F];
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {
+				return sample_rates [(int) Version,
+					(int) (flags >> 10) & 0x03];
+			}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return ChannelMode == ChannelMode.SingleChannel ? 1 : 2;}
+		}
+		
+		/// <summary>
+		///    Gets the length of the frames in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the length of the
+		///    frames in the audio represented by the current instance.
+		/// </value>
+		public int AudioFrameLength {
+			get {
+				switch (AudioLayer)
+				{
+					case 1:
+						return 48000  * AudioBitrate /
+							AudioSampleRate +
+							(IsPadded ? 4 : 0);
+					case 2:
+						return 144000 * AudioBitrate /
+							AudioSampleRate +
+							(IsPadded ? 1 : 0);
+					case 3:
+						if (Version == Version.Version1)
+							goto case 2;
+						
+						return 72000 * AudioBitrate /
+							AudioSampleRate +
+							(IsPadded ? 1 : 0);
+					default: return 0;
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    If <see cref="XingHeader" /> is equal to <see
+		///    cref="XingHeader.Unknown" /> and <see
+		///    cref="SetStreamLength" /> has not been called, this value
+		///    will not be correct.
+		///    If <see cref="VBRIHeader" /> is equal to <see
+		///    cref="VBRIHeader.Unknown" /> and <see
+		///    cref="SetStreamLength" /> has not been called, this value
+		///    will not be correct.
+		/// </remarks>
+		public TimeSpan Duration {
+			get {
+				if (duration > TimeSpan.Zero)
+					return duration;
+					
+				if (xing_header.TotalFrames > 0) {
+					// Read the length and the bitrate from
+					// the Xing header.
+					
+					double time_per_frame = (double)
+						block_size [(int) Version,
+						AudioLayer] / (double)
+						AudioSampleRate;
+					
+					duration = TimeSpan.FromSeconds (
+						time_per_frame *
+						XingHeader.TotalFrames);
+				} else if (vbri_header.TotalFrames > 0) {
+					// Read the length and the bitrate from
+					// the VBRI header.
+
+					double time_per_frame =
+						(double) block_size [
+							(int) Version, AudioLayer]
+						/ (double) AudioSampleRate;
+
+					duration = TimeSpan.FromSeconds (
+						Math.Round (time_per_frame *
+							VBRIHeader.TotalFrames));
+				} else if (AudioFrameLength > 0 &&
+					AudioBitrate > 0) {
+					// Since there was no valid Xing or VBRI
+					// header found, we hope that we're in a
+					// constant bitrate file.
+					
+					int frames = (int) (stream_length
+						 / AudioFrameLength + 1);
+					
+					duration = TimeSpan.FromSeconds (
+						(double) (AudioFrameLength *
+						frames) / (double)
+						(AudioBitrate * 125) + 0.5);
+				}
+				
+				return duration;
+			}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {
+				System.Text.StringBuilder builder =
+					new System.Text.StringBuilder ();
+				
+				builder.Append ("MPEG Version ");
+				switch (Version)
+				{
+				case Version.Version1:
+					builder.Append ("1");
+					break;
+				case Version.Version2:
+					builder.Append ("2");
+					break;
+				case Version.Version25:
+					builder.Append ("2.5");
+					break;
+				}
+				builder.Append (" Audio, Layer ");
+				builder.Append (AudioLayer);
+				
+				if (xing_header.Present || vbri_header.Present)
+					builder.Append (" VBR");
+				
+				return builder.ToString ();
+			}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the audio represented by the current
+		///    instance is protected.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    audio represented by the current instance is protected.
+		/// </value>
+		public bool IsProtected {
+			get {return ((flags >>16) & 1) == 0;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the audio represented by the current
+		///    instance is padded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    audio represented by the current instance is padded.
+		/// </value>
+		public bool IsPadded {
+			get {return ((flags >> 9) & 1) == 1;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the audio represented by the current
+		///    instance is copyrighted.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    audio represented by the current instance is copyrighted.
+		/// </value>
+		public bool IsCopyrighted {
+			get {return ((flags >> 3) & 1) == 1;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the audio represented by the current
+		///    instance is original.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    audio represented by the current instance is original.
+		/// </value>
+		public bool IsOriginal {
+			get {return ((flags >> 2) & 1) == 1;}
+		}
+		
+		/// <summary>
+		///    Gets the MPEG audio channel mode of the audio represented
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ChannelMode" /> value indicating the MPEG
+		///    audio channel mode of the audio represented by the
+		///    current instance.
+		/// </value>
+		public ChannelMode ChannelMode {
+			get {return (ChannelMode) ((flags >> 14) & 0x03);}
+		}
+		
+		/// <summary>
+		///    Gets the Xing header found in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="XingHeader" /> object containing the Xing
+		///    header found in the audio represented by the current
+		///    instance, or <see cref="XingHeader.Unknown" /> if no
+		///    header was found.
+		/// </value>
+		public XingHeader XingHeader {
+			get {return xing_header;}
+		}
+		
+		/// <summary>
+		///    Gets the VBRI header found in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="VBRIHeader" /> object containing the VBRI
+		///    header found in the audio represented by the current
+		///    instance, or <see cref="VBRIHeader.Unknown" /> if no
+		///    header was found.
+		/// </value>
+		public VBRIHeader VBRIHeader {
+			get {return vbri_header;}
+		}
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Sets the length of the audio stream represented by the
+		///    current instance.
+		/// </summary>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value specifying the length in
+		///    bytes of the audio stream represented by the current
+		///    instance.
+		/// </param>
+		/// <remarks>
+		///    The this value has been set, <see cref="Duration" /> will
+		///    return an incorrect value.
+		/// </remarks>
+		public void SetStreamLength (long streamLength)
+		{
+			this.stream_length = streamLength;
+			
+			// Force the recalculation of duration if it depends on
+			// the stream length.
+			if (xing_header.TotalFrames == 0 ||
+				vbri_header.TotalFrames == 0)
+				duration = TimeSpan.Zero;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Searches for an audio header in a <see cref="TagLib.File"
+		///    /> starting at a specified position and searching through
+		///    a specified number of bytes.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="AudioHeader" /> object in which the found
+		///    header will be stored.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to search.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    in <paramref name="file" /> at which to start searching.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the maximum number
+		///    of bytes to search before aborting.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not a
+		///    header was found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public static bool Find (out AudioHeader header,
+		                         TagLib.File file, long position,
+		                         int length)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			long end = position + length;
+			header = AudioHeader.Unknown;
+			
+			file.Seek (position);
+			
+			ByteVector buffer = file.ReadBlock (3);
+			
+			if (buffer.Count < 3)
+				return false;
+			
+			do {
+				file.Seek (position + 3);
+				buffer = buffer.Mid (buffer.Count - 3);
+				buffer.Add (file.ReadBlock (
+					(int) File.BufferSize));
+				
+				for (int i = 0; i < buffer.Count - 3 &&
+					(length < 0 || position + i < end); i++)
+					if (buffer [i] == 0xFF &&
+						buffer [i + 1] > 0xE0)
+						try {
+							header = new AudioHeader (
+								buffer.Mid (i, 4),
+								file, position + i);
+							return true;
+						} catch (CorruptFileException) {
+						}
+				
+				position += File.BufferSize;
+			} while (buffer.Count > 3 && (length < 0 || position < end));
+			
+			return false;
+		}
+		
+		/// <summary>
+		///    Searches for an audio header in a <see cref="TagLib.File"
+		///    /> starting at a specified position and searching to the
+		///    end of the file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="AudioHeader" /> object in which the found
+		///    header will be stored.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to search.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the seek position
+		///    in <paramref name="file" /> at which to start searching.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not a
+		///    header was found.
+		/// </returns>
+		/// <remarks>
+		///    Searching to the end of the file can be very, very slow
+		///    especially for corrupt or non-MPEG files. It is
+		///    recommended to use <see
+		///    cref="Find(AudioHeader,TagLib.File,long,int)" />
+		///    instead.
+		/// </remarks>
+		public static bool Find (out AudioHeader header,
+		                         TagLib.File file, long position)
+		{
+			return Find (out header, file, position, -1);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg/File.cs b/lib/TagLib/TagLib/Mpeg/File.cs
new file mode 100644
index 0000000..c8fc3fd
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg/File.cs
@@ -0,0 +1,697 @@
+//
+// File.cs: Provides tagging and properties support for MPEG-1, MPEG-2, and
+// MPEG-2.5 audio files.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg {
+	/// <summary>
+	///    Indicates the type of marker found in a MPEG file.
+	/// </summary>
+	public enum Marker {
+		/// <summary>
+		///    An invalid marker.
+		/// </summary>
+		Corrupt = -1,
+		
+		/// <summary>
+		///    A zero value marker.
+		/// </summary>
+		Zero = 0,
+		
+		/// <summary>
+		///   A marker indicating a system sync packet.
+		/// </summary>
+		SystemSyncPacket = 0xBA,
+		
+		/// <summary>
+		///   A marker indicating a video sync packet.
+		/// </summary>
+		VideoSyncPacket = 0xB3,
+		
+		/// <summary>
+		///   A marker indicating a system packet.
+		/// </summary>
+		SystemPacket = 0xBB,
+		
+		/// <summary>
+		///   A marker indicating a padding packet.
+		/// </summary>
+		PaddingPacket = 0xBE,
+		
+		/// <summary>
+		///   A marker indicating a audio packet.
+		/// </summary>
+		AudioPacket = 0xC0,
+		
+		/// <summary>
+		///   A marker indicating a video packet.
+		/// </summary>
+		VideoPacket = 0xE0,
+		
+		/// <summary>
+		///   A marker indicating the end of a stream.
+		/// </summary>
+		EndOfStream = 0xB9
+	}
+	
+	/// <summary>
+	///    This class extends <see cref="TagLib.NonContainer.File" /> to
+	///    provide tagging and properties support for MPEG-1, MPEG-2, and
+	///    MPEG-2.5 video files.
+	/// </summary>
+	/// <remarks>
+	///    A <see cref="TagLib.Id3v1.Tag" /> and <see
+	///    cref="TagLib.Id3v2.Tag" /> will be added automatically to any
+	///    file that doesn't contain one. This change does not effect the
+	///    file until it is saved and can be reversed using the following
+	///    method:
+	///    <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
+	/// </remarks>
+	[SupportedMimeType("taglib/mpg", "mpg")]
+	[SupportedMimeType("taglib/mpeg", "mpeg")]
+	[SupportedMimeType("taglib/mpe", "mpe")]
+	[SupportedMimeType("taglib/mpv2", "mpv2")]
+	[SupportedMimeType("taglib/m2v", "m2v")]
+	[SupportedMimeType("video/x-mpg")]
+	[SupportedMimeType("video/mpeg")]
+	public class File : TagLib.NonContainer.File
+	{
+		#region Private Static Fields
+		
+		private static readonly ByteVector MarkerStart =
+			new byte [] {0, 0, 1};
+		
+		#endregion
+		
+		
+		
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the MPEG version.
+		/// </summary>
+		private Version version;
+		
+		/// <summary>
+		///    Contains the first audio header.
+		/// </summary>
+		private AudioHeader audio_header;
+		
+		/// <summary>
+		///    Contains the first video header.
+		/// </summary>
+		private VideoHeader video_header;
+		
+		/// <summary>
+		///    Indicates whether or not audio was found.
+		/// </summary>
+		private bool video_found = false;
+		
+		/// <summary>
+		///    Indicates whether or not video was found.
+		/// </summary>
+		private bool audio_found = false;
+		
+		/// <summary>
+		///    Contains the start time of the file.
+		/// </summary>
+		private double? start_time = null;
+		
+		/// <summary>
+		///    Contains the end time of the file.
+		/// </summary>
+		private double end_time;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle)
+			: base (abstraction, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: base (abstraction)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    If a <see cref="TagLib.Id3v2.Tag" /> is added to the
+		///    current instance, it will be placed at the start of the
+		///    file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
+		///    <see cref="TagLib.Ape.Tag" /> will be added to the end of
+		///    the file. All other tag types will be ignored.
+		/// </remarks>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
+			
+			if (t != null || !create)
+				return t;
+			
+			switch (type)
+			{
+			case TagTypes.Id3v1:
+				return EndTag.AddTag (type, Tag);
+			
+			case TagTypes.Id3v2:
+				return EndTag.AddTag (type, Tag);
+			
+			case TagTypes.Ape:
+				return EndTag.AddTag (type, Tag);
+			
+			default:
+				return null;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadStart (long start,
+		                                   ReadStyle propertiesStyle)
+		{
+			if (propertiesStyle == ReadStyle.None)
+				return;
+			
+			FindMarker (ref start, Marker.SystemSyncPacket);
+			ReadSystemFile (start);
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadEnd (long end,
+		                                 ReadStyle propertiesStyle)
+		{
+			// Make sure we have ID3v1 and ID3v2 tags.
+			GetTag (TagTypes.Id3v1, true);
+			GetTag (TagTypes.Id3v2, true);
+			
+			if (propertiesStyle == ReadStyle.None ||
+				start_time == null)
+				return;
+			
+			RFindMarker (ref end, Marker.SystemSyncPacket);
+			
+			end_time = ReadTimestamp (end + 4);
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		protected override Properties ReadProperties (long start,
+		                                              long end,
+		                                              ReadStyle propertiesStyle)
+		{
+			TimeSpan duration = start_time == null ?
+				TimeSpan.Zero : TimeSpan.FromSeconds (
+					end_time - (double) start_time);
+			
+			return new Properties (duration, video_header,
+				audio_header);
+		}
+		
+		/// <summary>
+		///    Gets the marker at a specified position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the postion in the
+		///    file represented by the current instance at which to
+		///    read.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Marker" /> value containing the type of
+		///    marker found at the specified position.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    A valid marker does not exist at the specified position.
+		/// </exception>
+		protected Marker GetMarker (long position)
+		{
+			Seek (position);
+			ByteVector identifier = ReadBlock (4);
+			
+			if (identifier.Count == 4 && identifier.StartsWith (
+				MarkerStart))
+				return (Marker) identifier [3];
+			
+			throw new CorruptFileException (
+				"Invalid marker at position " + position);
+		}
+		
+		/// <summary>
+		///    Finds the next marker starting at a specified position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying the
+		///    position at which to start searching. This value
+		///    is updated to the position of the found marker.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Marker" /> value containing the type of
+		///    marker found at the specified position.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    A valid marker could not be found.
+		/// </exception>
+		protected Marker FindMarker (ref long position)
+		{
+			position = Find (MarkerStart, position);
+			if (position < 0)
+				throw new CorruptFileException (
+					"Marker not found");
+			
+			return GetMarker (position);
+		}
+		
+		/// <summary>
+		///    Finds the next marker of a specified type, starting at a
+		///    specified position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying the
+		///    position at which to start searching. This value
+		///    is updated to the position of the found marker.
+		/// </param>
+		/// <param name="marker">
+		///    A <see cref="Marker" /> value specifying the type of
+		///    marker to search for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Marker" /> value containing the type of
+		///    marker found at the specified position. This value will
+		///    be identical to <paramref name="marker" />.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    A valid marker could not be found.
+		/// </exception>
+		protected Marker FindMarker (ref long position, Marker marker)
+		{
+			ByteVector packet = new ByteVector (MarkerStart);
+			packet.Add ((byte) marker);
+			position = Find (packet, position);
+			
+			if (position < 0)
+				throw new CorruptFileException (
+					"Marker not found");
+			
+			return GetMarker (position);
+		}
+		
+		/// <summary>
+		///    Finds the previous marker of a specified type, starting
+		///    at a specified position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying the
+		///    position at which to start searching. This value
+		///    is updated to the position of the found marker.
+		/// </param>
+		/// <param name="marker">
+		///    A <see cref="Marker" /> value specifying the type of
+		///    marker to search for.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Marker" /> value containing the type of
+		///    marker found at the specified position. This value will
+		///    be identical to <paramref name="marker" />.
+		/// </returns>
+		/// <exception cref="CorruptFileException">
+		///    A valid marker could not be found.
+		/// </exception>
+		protected Marker RFindMarker (ref long position, Marker marker)
+		{
+			ByteVector packet = new ByteVector (MarkerStart);
+			packet.Add ((byte) marker);
+			position = RFind (packet, position);
+			
+			if (position < 0)
+				throw new CorruptFileException (
+					"Marker not found");
+			
+			return GetMarker (position);
+		}
+		
+		/// <summary>
+		///    Reads the contents of the file as a system file, starting
+		///    at a specified position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying the postion in the
+		///    file represented by the current instance at which to
+		///    start reading.
+		/// </param>
+		/// <remarks>
+		///    This method will stop when it has read both an audio and
+		///    a video header, or once it's read 100 packets. This is to
+		///    prevent the entire file from being read if it lacks one
+		///    type of stream.
+		/// </remarks>
+		protected void ReadSystemFile (long position)
+		{
+			int sanity_limit = 100;
+			
+			for (int i = 0; i < sanity_limit && (start_time == null ||
+				!audio_found || !video_found); i ++) {
+				
+				Marker marker = FindMarker (ref position);
+				
+				switch (marker)
+				{
+				case Marker.SystemSyncPacket:
+					ReadSystemSyncPacket (ref position);
+					break;
+				
+				case Marker.SystemPacket:
+				case Marker.PaddingPacket:
+					Seek (position + 4);
+					position += ReadBlock (2).ToUShort () +
+						6;
+					break;
+				
+				case Marker.VideoPacket:
+					ReadVideoPacket (ref position);
+					break;
+				
+				case Marker.AudioPacket:
+					ReadAudioPacket (ref position);
+					break;
+				
+				case Marker.EndOfStream:
+					return;
+				
+				default:
+					position += 4;
+					break;
+				}
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Reads an audio packet, assigning the audio header and
+		///    advancing the position to the next packet position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying the
+		///    position at which to start reading the packet. This value
+		///    is updated to the position of the next packet.
+		/// </param>
+		void ReadAudioPacket (ref long position)
+		{
+			Seek (position + 4);
+			int length = ReadBlock (2).ToUShort ();
+			
+			if (!audio_found)
+				audio_found = AudioHeader.Find (
+					out audio_header, this, position + 15,
+					length - 9);
+			position += length;
+		}
+		
+		/// <summary>
+		///    Reads a video packet, assigning the video header and
+		///    advancing the position to the next packet position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying the
+		///    position at which to start reading the packet. This value
+		///    is updated to the position of the next packet.
+		/// </param>
+		void ReadVideoPacket (ref long position)
+		{
+			Seek (position + 4);
+			int length = ReadBlock (2).ToUShort ();
+			long offset = position + 6;
+			
+			while (!video_found && offset < position + length)
+				if (FindMarker (ref offset) ==
+					Marker.VideoSyncPacket) {
+					video_header = new VideoHeader (this,
+						offset + 4);
+					video_found = true;
+				} else {
+					// advance the offset by 6 bytes, so the next iteration of the
+					// loop won't find the same marker and get stuck.  6 bytes because findMarker is a
+					// generic find that could get both PES packets and Stream packets, the smallest
+					// posible pes packet with a size =0 would be 6 bytes.
+					offset += 6;
+				}
+			
+			position += length;
+		}
+		
+		/// <summary>
+		///    Reads a system sync packet, filling in version
+		///    information and the first timestamp value, advancing the
+		///    position to the next packet position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying the
+		///    position at which to start reading the packet. If the
+		///    method is called without exception, this is updated to
+		///    the position of the next packet.
+		/// </param>
+		/// <exception cref="UnsupportedFormatException">
+		///    The MPEG version contained in the packet is unknown.
+		/// </exception>
+		void ReadSystemSyncPacket (ref long position)
+		{
+			int packet_size = 0;
+			Seek (position + 4);
+			byte version_info = ReadBlock (1) [0];
+			
+			if ((version_info & 0xF0) == 0x20) {
+				version = Version.Version1;
+				packet_size = 12;
+			} else if ((version_info & 0xC0) == 0x40) {
+				version = Version.Version2;
+				Seek (position + 13);
+				packet_size = 14 + (ReadBlock (1) [0] & 0x07);
+			} else
+				throw new UnsupportedFormatException (
+					"Unknown MPEG version.");
+			
+			if (start_time == null)
+				start_time = ReadTimestamp (position + 4);
+			
+			position += packet_size;
+		}
+		
+		/// <summary>
+		///    Reads an MPEG timestamp from a specified position in the
+		///    file represented by the current instance.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value containing the position in
+		///    the file at which to read. This should be immediately
+		///    following a system sync packet marker.
+		/// </param>
+		/// <returns>
+		///    A <see cref="double" /> value containing the read time in
+		///    seconds.
+		/// </returns>
+		private double ReadTimestamp (long position)
+		{
+			double high;
+			uint low;
+			
+			Seek (position);
+			
+			if (version == Version.Version1) {
+				ByteVector data = ReadBlock (5);
+				high = (double) ((data [0] >> 3) & 0x01);
+				
+				low =  ((uint)((data [0] >> 1) & 0x03) << 30) |
+					(uint) (data [1] << 22) |
+					(uint)((data [2] >> 1) << 15) |
+					(uint) (data [3] << 7) |
+					(uint) (data [4] << 1);
+			} else {
+				ByteVector data = ReadBlock (6);
+				high = (double) ((data [0] & 0x20) >> 5);
+				
+				low =  ((uint) ((data [0] & 0x18) >> 3) << 30) |
+					(uint) ((data [0] & 0x03) << 28) |
+					(uint)  (data [1] << 20) |
+					(uint) ((data [2] & 0xF8) << 12) |
+					(uint) ((data [2] & 0x03) << 13) |
+					(uint)  (data [3] << 5) |
+					(uint)  (data [4] >> 3);
+			}
+			
+			return (((high * 0x10000) * 0x10000) + low) / 90000.0;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg/VBRIHeader.cs b/lib/TagLib/TagLib/Mpeg/VBRIHeader.cs
new file mode 100644
index 0000000..956373c
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg/VBRIHeader.cs
@@ -0,0 +1,205 @@
+//
+// VBRIHeader.cs: Provides information about a variable bitrate MPEG audio
+// stream encoded with the Fraunhofer Encoder.
+//
+// Author:
+//   Helmut Wahrmann
+//
+// Original Source:
+//   XingHeader.cs
+//
+// Copyright (C) 2007 Helmut Wahrmann
+// Copyright (C) 2005-2007 Brian Nickel
+// 
+// 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Mpeg {
+	/// <summary>
+	///    This structure provides information about a variable bitrate MPEG
+	///    audio stream encoded by the Fraunhofer Encoder.
+	/// </summary>
+	public struct VBRIHeader
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the frame count.
+		/// </summary>
+		private uint frames;
+		
+		/// <summary>
+		///    Contains the stream size.
+		/// </summary>
+		private uint size;
+		
+		/// <summary>
+		///    Indicates that a physical VBRI header is present.
+		/// </summary>
+		private bool present;
+		
+#endregion
+		
+		
+		
+#region Public Fields
+		
+		/// <summary>
+		///    Contains te VBRI identifier.
+		/// </summary>
+		/// <value>
+		///    "VBRI"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "VBRI";
+		
+		/// <summary>
+		///    An empty and unset VBRI header.
+		/// </summary>
+		public static readonly VBRIHeader Unknown = new VBRIHeader (0, 0);
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="VBRIHeader" /> with a specified frame count and
+		///    size.
+		/// </summary>
+		/// <param name="frame">
+		///    A <see cref="uint" /> value specifying the frame count of
+		///    the audio represented by the new instance.
+		/// </param>
+		/// <param name="size">
+		///    A <see cref="uint" /> value specifying the stream size of
+		///    the audio represented by the new instance.
+		/// </param>
+		private VBRIHeader (uint frame, uint size)
+		{
+			this.frames = frame;
+			this.size = size;
+			this.present = false;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="VBRIHeader" /> by reading its raw contents.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    VBRI header.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> does not start with <see
+		///    cref="FileIdentifier" />.
+		/// </exception>
+		public VBRIHeader (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			// Check to see if a valid VBRI header is available.
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"Not a valid VBRI header");
+			
+			// Size starts at Position 10
+			int position = 10;
+
+			size = data.Mid(position, 4).ToUInt();
+			position += 4;
+
+			// The number of Frames are found at Posistion 14
+			frames = data.Mid(position, 4).ToUInt();
+			position += 4;
+			
+			present = true;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the total number of frames in the file, as indicated
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    frames in the file, or <c>0</c> if not specified.
+		/// </value>
+		public uint TotalFrames {
+			get {return frames;}
+		}
+		
+		/// <summary>
+		///    Gets the total size of the file, as indicated by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the total size of
+		///    the file, or <c>0</c> if not specified.
+		/// </value>
+		public uint TotalSize {
+			get {return size;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not a physical VBRI header is present in
+		///    the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance represents a physical VBRI header.
+		/// </value>
+		public bool Present {
+			get {return present;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Gets the offset at which a VBRI header would appear in an
+		///    MPEG audio packet.
+		///    Always 32 bytes after the end of the first MPEG Header.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value indicating the offset in an
+		///    MPEG audio packet at which the VBRI header would appear.
+		/// </returns>
+		public static int VBRIHeaderOffset ()
+		{
+			// A VBRI header always appears 32 bytes after the end
+			// of the first MPEG Header. So it's position 36 (0x24).
+	  		return 0x24;
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg/VideoHeader.cs b/lib/TagLib/TagLib/Mpeg/VideoHeader.cs
new file mode 100644
index 0000000..5ab14dd
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg/VideoHeader.cs
@@ -0,0 +1,206 @@
+//
+// VideoHeader.cs: Provides information about an MPEG video stream.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg {
+	/// <summary>
+	///    This structure implements <see cref="IVideoCodec" /> and provides
+	///    information about an MPEG video stream.
+	/// </summary>
+	public struct VideoHeader : IVideoCodec
+	{
+		#region Private Static Fields
+		
+		/// <summary>
+		///    Contains frame rate values.
+		/// </summary>
+		private static readonly double[] frame_rates = new double[9] {
+			0, 24000d/1001d, 24, 25, 30000d/1001d, 30, 50,
+			60000d/1001d, 60
+		};
+		
+		#endregion
+		
+		
+		
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the video width.
+		/// </summary>
+		int width;
+		
+		/// <summary>
+		///    Contains the video height.
+		/// </summary>
+		int height;
+		
+		/// <summary>
+		///    Contains the index in <see cref="frame_rates" /> of the
+		///    video frame rate.
+		/// </summary>
+		int frame_rate_index;
+		
+		/// <summary>
+		///    Contains the video bitrate.
+		/// </summary>
+		int bitrate;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="VideoHeader" /> by reading it from a specified
+		///    location in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value indicating the position in
+		///    <paramref name="file" /> at which the header begins.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    Insufficient data could be read for the header.
+		/// </exception>
+		public VideoHeader (TagLib.File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Seek (position);
+			ByteVector data = file.ReadBlock (7);
+			
+			if (data.Count < 7)
+				throw new CorruptFileException (
+					"Insufficient data in header.");
+			
+			width = data.Mid (0, 2).ToUShort () >> 4;
+			height = data.Mid (1, 2).ToUShort () & 0x0FFF;
+			frame_rate_index = data [3] & 0x0F;
+			bitrate = (int) ((data.Mid (4, 3).ToUInt () >> 6) &
+				0x3FFFF);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TimeSpan.Zero" />.
+		/// </value>
+		public TimeSpan Duration {
+			get {return TimeSpan.Zero;}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Video" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Video;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {return "MPEG Video";}
+		}
+		
+		/// <summary>
+		///    Gets the width of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the width of the
+		///    video represented by the current instance.
+		/// </value>
+		public int VideoWidth {
+			get {return width;}
+		}
+		
+		/// <summary>
+		///    Gets the height of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the height of the
+		///    video represented by the current instance.
+		/// </value>
+		public int VideoHeight {
+			get {return height;}
+		}
+		
+		/// <summary>
+		///    Gets the frame rate of the video represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="double" /> value containing the frame rate
+		///    of the video represented by the current instance.
+		/// </value>
+		public double VideoFrameRate {
+			get {
+				return frame_rate_index < 9 ? 
+					frame_rates [frame_rate_index] : 0;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the bitrate of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    video represented by the current instance.
+		/// </value>
+		public int VideoBitrate {
+			get {return bitrate;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg/XingHeader.cs b/lib/TagLib/TagLib/Mpeg/XingHeader.cs
new file mode 100644
index 0000000..116fd95
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg/XingHeader.cs
@@ -0,0 +1,221 @@
+//
+// XingHeader.cs: Provides information about a variable bitrate MPEG audio
+// stream.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   xingheader.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2003 by Ismael Orenstein (Original 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
+//
+
+using System.Collections;
+using System;
+
+namespace TagLib.Mpeg {
+	/// <summary>
+	///    This structure provides information about a variable bitrate MPEG
+	///    audio stream.
+	/// </summary>
+	public struct XingHeader
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the frame count.
+		/// </summary>
+		private uint frames;
+		
+		/// <summary>
+		///    Contains the stream size.
+		/// </summary>
+		private uint size;
+		
+		/// <summary>
+		///    Indicates that a physical Xing header is present.
+		/// </summary>
+		private bool present;
+		
+		#endregion
+		
+		
+		
+		#region Public Fields
+		
+		/// <summary>
+		///    Contains te Xing identifier.
+		/// </summary>
+		/// <value>
+		///    "Xing"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "Xing";
+		
+		/// <summary>
+		///    An empty and unset Xing header.
+		/// </summary>
+		public static readonly XingHeader Unknown = new XingHeader (0, 0);
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="XingHeader" /> with a specified frame count and
+		///    size.
+		/// </summary>
+		/// <param name="frame">
+		///    A <see cref="uint" /> value specifying the frame count of
+		///    the audio represented by the new instance.
+		/// </param>
+		/// <param name="size">
+		///    A <see cref="uint" /> value specifying the stream size of
+		///    the audio represented by the new instance.
+		/// </param>
+		private XingHeader (uint frame, uint size)
+		{
+			this.frames = frame;
+			this.size = size;
+			this.present = false;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="XingHeader" /> by reading its raw contents.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    Xing header.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> does not start with <see
+		///    cref="FileIdentifier" />.
+		/// </exception>
+		public XingHeader (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			// Check to see if a valid Xing header is available.
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"Not a valid Xing header");
+			
+			int position = 8;
+			
+			if ((data [7] & 0x01) != 0) {
+				frames = data.Mid (position, 4).ToUInt ();
+				position += 4;
+			} else
+				frames = 0;
+			
+			if ((data [7] & 0x02) != 0) {
+				size = data.Mid (position, 4).ToUInt ();
+				position += 4;
+			} else
+				size = 0;
+			
+			present = true;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the total number of frames in the file, as indicated
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    frames in the file, or <c>0</c> if not specified.
+		/// </value>
+		public uint TotalFrames {
+			get {return frames;}
+		}
+		
+		/// <summary>
+		///    Gets the total size of the file, as indicated by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the total size of
+		///    the file, or <c>0</c> if not specified.
+		/// </value>
+		public uint TotalSize {
+			get {return size;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not a physical Xing header is present in
+		///    the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance represents a physical Xing header.
+		/// </value>
+		public bool Present {
+			get {return present;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Gets the offset at which a Xing header would appear in an
+		///    MPEG audio packet based on the version and channel mode.
+		/// </summary>
+		/// <param name="version">
+		///    A <see cref="Version" /> value specifying the version of
+		///    the MPEG audio packet.
+		/// </param>
+		/// <param name="channelMode">
+		///    A <see cref="ChannelMode" /> value specifying the channel
+		///    mode of the MPEG audio packet.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value indicating the offset in an
+		///    MPEG audio packet at which the Xing header would appear.
+		/// </returns>
+		public static int XingHeaderOffset (Version version,
+		                                    ChannelMode channelMode)
+		{
+			bool single_channel =
+				channelMode == ChannelMode.SingleChannel;
+			
+			if (version == Version.Version1)
+				return single_channel ? 0x15 : 0x24;
+			else
+				return single_channel ? 0x0D : 0x15;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/AppleTag.cs b/lib/TagLib/TagLib/Mpeg4/AppleTag.cs
new file mode 100644
index 0000000..29e7e6c
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/AppleTag.cs
@@ -0,0 +1,1434 @@
+//
+// AppleTag.cs: Provides support for processing Apple "ilst" tags.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="TagLib.Tag" /> to provide support
+	///    for processing Apple "ilst" tags.
+	/// </summary>
+	public class AppleTag : TagLib.Tag, IEnumerable<Box>
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the ISO meta box in which that tag will be
+		///    stored.
+		/// </summary>
+		private IsoMetaBox meta_box;
+		
+		/// <summary>
+		///    Contains the ILST box which holds all the values.
+		/// </summary>
+		private AppleItemListBox ilst_box;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleTag" /> for a specified ISO user data box.
+		/// </summary>
+		/// <param name="box">
+		///    A <see cref="IsoUserDataBox" /> from which the tag is to
+		///    be read.
+		/// </param>
+		public AppleTag (IsoUserDataBox box)
+		{
+			if (box == null)
+				throw new ArgumentNullException ("box");
+			
+			meta_box = box.GetChild (BoxType.Meta) as IsoMetaBox;
+			if (meta_box == null) {
+				meta_box = new IsoMetaBox ("mdir", null);
+				box.AddChild (meta_box);
+			}
+			
+			ilst_box = meta_box.GetChild (BoxType.Ilst)
+				as AppleItemListBox;
+			
+			if (ilst_box == null) {
+				ilst_box = new AppleItemListBox ();
+				meta_box.AddChild (ilst_box);
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets and sets whether or not the album described by the
+		///    current instance is a compilation.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    album described by the current instance is a compilation.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "cpil" data box.
+		/// </remarks>
+		public bool IsCompilation {
+			get {
+				foreach (AppleDataBox box in DataBoxes (
+					BoxType.Cpil))
+					return box.Data.ToUInt () != 0;
+				
+				return false;
+			}
+			set {
+				SetData (BoxType.Cpil, new ByteVector(
+					(byte) (value ? 1 : 0)),
+					(uint) AppleDataBox.FlagType.ForTempo);
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets all data boxes that match any of the provided types.
+		/// </summary>
+		/// <param name="types">
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating a list
+		///    of box types to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    matching boxes.
+		/// </returns>
+		public IEnumerable<AppleDataBox> DataBoxes (IEnumerable<ByteVector> types)
+		{
+			// Check each box to see if the match any of the
+			// provided types. If a match is found, loop through the
+			// children and add any data box.
+			foreach (Box box in ilst_box.Children)
+				foreach (ByteVector v in types) {
+					if (FixId (v) != box.BoxType)
+						continue;
+					foreach (Box data_box in box.Children) {
+						AppleDataBox adb = data_box as
+							AppleDataBox;
+						if (adb != null)
+							yield return adb;
+					}
+				}
+		}
+		
+		/// <summary>
+		///    Gets all data boxes that match any of the provided types.
+		/// </summary>
+		/// <param name="types">
+		///    A <see cref="ByteVector[]" /> containing list of box
+		///    types to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    matching boxes.
+		/// </returns>
+		public IEnumerable<AppleDataBox> DataBoxes (params ByteVector [] types)
+		{
+			return DataBoxes (types as IEnumerable<ByteVector>);
+		}
+		
+		/// <summary>
+		///    Gets all custom data boxes that match the specified mean
+		///    and name pair.
+		/// </summary>
+		/// <param name="mean">
+		///    A <see cref="string" /> object containing the "mean" to
+		///    match.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object containing the name to
+		///    match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    matching boxes.
+		/// </returns>
+		public IEnumerable<AppleDataBox> DataBoxes (string mean,
+		                                            string name)
+		{
+			// These children will have a box type of "----"
+			foreach (Box box in ilst_box.Children) {
+				if (box.BoxType != BoxType.DASH)
+					continue;
+				
+				// Get the mean and name boxes, make sure
+				// they're legit, and make sure that they match
+				// what we want. Then loop through and add all
+				// the data box children to our output.
+				AppleAdditionalInfoBox mean_box =
+					(AppleAdditionalInfoBox)
+					box.GetChild (BoxType.Mean);
+				AppleAdditionalInfoBox name_box =
+					(AppleAdditionalInfoBox)
+					box.GetChild (BoxType.Name);
+				
+				if (mean_box == null || name_box == null ||
+					mean_box.Text != mean ||
+					name_box.Text != name)
+					continue;
+				
+				foreach (Box data_box in box.Children) {
+					AppleDataBox adb =
+						data_box as AppleDataBox;
+					
+					if (adb != null)
+						yield return adb;
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Gets all text values contained in a specified box type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the box
+		///    type to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing text from all
+		///    matching boxes.
+		/// </returns>
+		public string [] GetText (ByteVector type) {
+			List<string> result = new List<string> ();
+			foreach (AppleDataBox box in DataBoxes (type)) {
+				if (box.Text == null)
+					continue;
+				
+				foreach (string text in box.Text.Split (';'))
+					result.Add (text.Trim ());
+			}
+			
+			return result.ToArray ();
+		}
+		
+		/// <summary>
+		///    Sets the data for a specified box type to a collection of
+		///    boxes.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the type to
+		///    add to the new instance.
+		/// </param>
+		/// <param name="boxes">
+		///    A <see cref="AppleDataBox[]" /> containing boxes to add
+		///    for the specified type.
+		/// </param>
+		public void SetData (ByteVector type, AppleDataBox [] boxes)
+		{
+			// Fix the type.
+			type = FixId (type);
+			
+			bool added = false;
+			
+			foreach (Box box in ilst_box.Children)
+				if (type == box.BoxType) {
+					
+					// Clear the box's children.
+					box.ClearChildren ();
+					
+					// If we've already added new childen,
+					// continue.
+					if (added)
+						continue;
+					
+					added = true;
+					
+					// Add the children.
+					foreach (AppleDataBox b in boxes)
+						box.AddChild (b);
+				}
+			
+			if (added)
+				return;
+			
+			Box box2 = new AppleAnnotationBox (type);
+			ilst_box.AddChild (box2);
+			
+			foreach (AppleDataBox b in boxes)
+				box2.AddChild (b);
+		}
+		
+		/// <summary>
+		///    Sets the data for a specified box type using values from
+		///    a <see cref="ByteVectorCollection" /> object.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the type to
+		///    add to the new instance.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    data to add for the specified type.
+		/// </param>
+		/// <param name="flags">
+		///    A <see cref="uint" /> value containing flags to use for
+		///    the added boxes.
+		/// </param>
+		public void SetData (ByteVector type, ByteVectorCollection data,
+		                     uint flags)
+		{
+			if (data == null || data.Count == 0) {
+				ClearData (type);
+				return;
+			}
+			
+			AppleDataBox [] boxes = new AppleDataBox [data.Count];
+			for (int i = 0; i < data.Count; i ++)
+				boxes [i] = new AppleDataBox (data [i], flags);
+			
+			SetData (type, boxes);
+		}
+		
+		/// <summary>
+		///    Sets the data for a specified box type using a single
+		///    <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the type to
+		///    add to the new instance.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing data to add
+		///    for the specified type.
+		/// </param>
+		/// <param name="flags">
+		///    A <see cref="uint" /> value containing flags to use for
+		///    the added box.
+		/// </param>
+		public void SetData (ByteVector type, ByteVector data,
+		                     uint flags)
+		{
+			if (data == null || data.Count == 0)
+				ClearData (type);
+			else
+				SetData (type, new ByteVectorCollection (data),
+					flags);
+		}
+      
+		/// <summary>
+		///    Sets the text for a specified box type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the type to
+		///    add to the new instance.
+		/// </param>
+		/// <param name="text">
+		///    A <see cref="string[]" /> containing text to store.
+		/// </param>
+		public void SetText (ByteVector type, string [] text)
+		{
+			// Remove empty data and return.
+			if (text == null) {
+				ilst_box.RemoveChild (FixId (type));
+				return;
+			}
+			
+			SetText (type, string.Join ("; ", text));
+		}
+		
+		/// <summary>
+		///    Sets the text for a specified box type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the type to
+		///    add to the new instance.
+		/// </param>
+		/// <param name="text">
+		///    A <see cref="string" /> object containing text to store.
+		/// </param>
+		public void SetText (ByteVector type, string text)
+		{
+			// Remove empty data and return.
+			if (string.IsNullOrEmpty (text)) {
+				ilst_box.RemoveChild (FixId (type));
+				return;
+			}
+			
+			ByteVectorCollection l = new ByteVectorCollection ();
+			l.Add (ByteVector.FromString (text, StringType.UTF8));
+			SetData (type, l, (uint)
+				AppleDataBox.FlagType.ContainsText);
+		}
+		
+		/// <summary>
+		///    Clears all data for a specified box type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the type of
+		///    box to remove from the current instance.
+		/// </param>
+		public void ClearData (ByteVector type)
+		{
+			ilst_box.RemoveChild (FixId (type));
+		}
+		
+		/// <summary>
+		///    Detaches the internal "ilst" box from its parent element.
+		/// </summary>
+		public void DetachIlst ()
+		{
+			meta_box.RemoveChild (ilst_box);
+		}
+		
+		/// <summary>
+		/// Gets the text string from a specific data box in a Dash (----) atom
+		/// </summary>
+		/// <param name="meanstring">String specifying text from mean box</param>
+		/// <param name="namestring">String specifying text from name box</param>
+		/// <returns>Text string from data box</returns>
+		public string GetDashBox(string meanstring, string namestring)
+		{
+			AppleDataBox data_box = GetDashAtoms(meanstring, namestring);
+			if (data_box != null) {
+				return data_box.Text;
+			} else {
+				return null;
+			}
+		}
+			
+		/// <summary>
+		/// Sets a specific strings in Dash (----) atom.  This method updates
+		/// and existing atom, or creates a new one.  If an empty datastring is
+		/// specified, the Dash box and its children are removed.
+		/// </summary>
+		/// <param name="meanstring">String specifying text for mean box</param>
+		/// <param name="namestring">String specifying text for name box</param>
+		/// <param name="datastring">String specifying text for data box</param>
+		public void SetDashBox(string meanstring, string namestring, string datastring)
+		{
+			AppleDataBox data_box = GetDashAtoms(meanstring, namestring);
+			
+			// If we did find a data_box and we have an empty datastring we should
+			// remove the entire dash box.
+			if (data_box != null && string.IsNullOrEmpty(datastring)) {
+				AppleAnnotationBox dash_box = GetParentDashBox(meanstring, namestring);
+				dash_box.ClearChildren();
+				ilst_box.RemoveChild(dash_box);
+				return;
+			}
+			
+			if (data_box != null) {
+				data_box.Text = datastring;
+			} else {
+				//Create the new boxes, should use 1 for text as a flag
+				AppleAdditionalInfoBox amean_box = new AppleAdditionalInfoBox(BoxType.Mean, 0, 1);
+				AppleAdditionalInfoBox aname_box = new AppleAdditionalInfoBox(BoxType.Name, 0, 1);
+				AppleDataBox adata_box = new AppleDataBox(BoxType.Data, 1);
+				amean_box.Text = meanstring;
+				aname_box.Text = namestring;
+				adata_box.Text = datastring;
+				AppleAnnotationBox whole_box = new AppleAnnotationBox(BoxType.DASH);
+				whole_box.AddChild(amean_box);
+				whole_box.AddChild(aname_box);
+				whole_box.AddChild(adata_box);
+				ilst_box.AddChild(whole_box);
+			}
+		}
+		
+		/// <summary>
+		/// Gets the AppleDataBox that corresponds to the specified mean and name values.
+		/// </summary>
+		/// <param name="meanstring">String specifying text for mean box</param>
+		/// <param name="namestring">String specifying text for name box</param>
+		/// <returns>Existing AppleDataBox or null if one does not exist</returns>
+		private AppleDataBox GetDashAtoms(string meanstring, string namestring)
+		{
+			foreach (Box box in ilst_box.Children) {
+				if (box.BoxType != BoxType.DASH)
+					continue;
+				
+				// Get the mean and name boxes, make sure
+				// they're legit, check the Text fields for
+				// a match.  If we have a match return
+				// the AppleDatabox containing the data
+				
+				AppleAdditionalInfoBox mean_box =
+					(AppleAdditionalInfoBox)
+					box.GetChild(BoxType.Mean);
+				AppleAdditionalInfoBox name_box =
+					(AppleAdditionalInfoBox)
+					box.GetChild(BoxType.Name);
+					
+				if (mean_box == null || name_box == null ||
+					mean_box.Text != meanstring ||
+					name_box.Text != namestring) {
+					continue;
+				} else {
+					return (AppleDataBox)box.GetChild(BoxType.Data);
+				}
+			}
+			// If we haven't returned the found box yet, there isn't one, return null
+			return null;
+		}
+		
+		/// <summary>
+		/// Returns the Parent Dash box object for a given mean/name combination
+		/// </summary>
+		/// <param name="meanstring">String specifying text for mean box</param>
+		/// <param name="namestring">String specifying text for name box</param>
+		/// <returns>AppleAnnotationBox object that is the parent for the mean/name combination</returns>
+		private AppleAnnotationBox GetParentDashBox(string meanstring, string namestring)
+		{
+			foreach (Box box in ilst_box.Children) {
+				if (box.BoxType != BoxType.DASH)
+					continue;
+				
+				// Get the mean and name boxes, make sure
+				// they're legit, check the Text fields for
+				// a match.  If we have a match return
+				// the AppleAnnotationBox that is the Parent
+				
+				AppleAdditionalInfoBox mean_box =
+					(AppleAdditionalInfoBox)
+					box.GetChild(BoxType.Mean);
+				AppleAdditionalInfoBox name_box =
+					(AppleAdditionalInfoBox)
+					box.GetChild(BoxType.Name);
+					
+				if (mean_box == null || name_box == null ||
+					mean_box.Text != meanstring ||
+					name_box.Text != namestring) {
+					continue;
+				} else {
+					return (AppleAnnotationBox)box;
+				}
+			}
+			// If we haven't returned the found box yet, there isn't one, return null
+			return null;
+		}
+		#endregion
+		
+		
+		
+		#region Internal Methods
+		
+		/// <summary>
+		///    Converts the provided ID into a readonly ID and fixes a
+		///    3 byte ID.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing an ID to
+		///    fix.
+		/// </param>
+		/// <returns>
+		///    A fixed <see cref="ReadOnlyByteVector" /> or <see
+		///    langword="null" /> if the ID could not be fixed.
+		/// </returns>
+		internal static ReadOnlyByteVector FixId (ByteVector id)
+		{
+			if (id.Count == 4) {
+				ReadOnlyByteVector roid =
+					id as ReadOnlyByteVector;
+				if (roid != null)
+					return roid;
+				
+				return new ReadOnlyByteVector (id);
+			}
+			
+			if (id.Count == 3)
+				return new ReadOnlyByteVector (
+					0xa9, id [0], id [1], id [2]);
+			
+			return null;
+		}
+		
+		#endregion
+		
+		
+		
+		#region IEnumerable<Box>
+		
+		/// <summary>
+		///    Gets an enumerator for enumerating through the tag's data
+		///    boxes.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the tag's data boxes.
+		/// </returns>
+		public IEnumerator<Box> GetEnumerator()
+		{
+			return ilst_box.Children.GetEnumerator();
+		}
+		
+		IEnumerator IEnumerable.GetEnumerator()
+		{
+			return ilst_box.Children.GetEnumerator();
+		}
+		
+		#endregion
+		
+		
+		
+		#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.Apple" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.Apple;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@nam" data box.
+		/// </remarks>
+		public override string Title {
+			get {
+				string [] text = GetText (BoxType.Nam);
+				return text.Length == 0 ? null : text [0];
+			}
+			set {
+				SetText (BoxType.Nam, value);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@ART" data box.
+		/// </remarks>
+		public override string [] Performers {
+			get {return GetText (BoxType.Art);}
+			set {SetText (BoxType.Art, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "aART" data box.
+		/// </remarks>
+		public override string [] AlbumArtists {
+			get {return GetText (BoxType.Aart);}
+			set {SetText(BoxType.Aart, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@wrt" data box.
+		/// </remarks>
+		public override string [] Composers {
+			get {return GetText (BoxType.Wrt);}
+			set {SetText (BoxType.Wrt, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@alb" data box.
+		/// </remarks>
+		public override string Album {
+			get {
+				string [] text = GetText (BoxType.Alb);
+				return text.Length == 0 ? null : text [0];
+			}
+			set {SetText (BoxType.Alb, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@cmt" data box.
+		/// </remarks>
+		public override string Comment {
+			get {
+				string [] text = GetText (BoxType.Cmt);
+				return text.Length == 0 ? null : text [0];
+			}
+			set {SetText (BoxType.Cmt, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@gen" and "gnre"
+		///    data boxes.
+		/// </remarks>
+		public override string [] Genres {
+			get {
+				string [] text = GetText (BoxType.Gen);
+				if (text.Length > 0)
+					return text;
+				
+				foreach (AppleDataBox box in DataBoxes (BoxType.Gnre)) {
+					if (box.Flags != (int) AppleDataBox
+						.FlagType.ContainsData)
+						continue;
+					
+					// iTunes stores genre's in the GNRE box
+					// as (ID3# + 1).
+					
+					ushort index = box.Data.ToUShort (true);
+					if (index == 0) continue;
+					
+					string str = TagLib.Genres
+						.IndexToAudio ((byte) (index - 1));
+					
+					if (str == null)
+						continue;
+					
+					text = new string [] {str};
+					break;
+				}
+				
+				return text;
+			}
+			set {
+				ClearData (BoxType.Gnre);
+				SetText (BoxType.Gen, value);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@day" data box.
+		/// </remarks>
+		public override uint Year {
+			get {
+				uint value;
+				foreach (AppleDataBox box in DataBoxes (BoxType.Day))
+					if (box.Text != null && (uint.TryParse (
+						box.Text, out value) ||
+						uint.TryParse (
+							box.Text.Length > 4 ?
+							box.Text.Substring (0, 4)
+							: box.Text, out value)))
+						return value;
+				
+				return 0;
+			}
+			set {
+				if (value == 0)
+					ClearData (BoxType.Day);
+				else
+					SetText (BoxType.Day, value.ToString (
+						CultureInfo.InvariantCulture));
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "trkn" data box.
+		/// </remarks>
+		public override uint Track {
+			get {
+				foreach (AppleDataBox box in DataBoxes (BoxType.Trkn))
+					if (box.Flags == (int)
+						AppleDataBox.FlagType.ContainsData &&
+						box.Data.Count >= 4)
+						return box.Data.Mid (2, 2).ToUShort ();
+				
+				return 0;
+			}
+			set {
+				uint count = TrackCount;
+				if (value == 0 && count == 0) {
+					ClearData (BoxType.Trkn);
+					return;
+				}
+				
+				ByteVector v = ByteVector.FromUShort (0);
+				v.Add (ByteVector.FromUShort ((ushort) value));
+				v.Add (ByteVector.FromUShort ((ushort) count));
+				v.Add (ByteVector.FromUShort (0));
+				
+				SetData (BoxType.Trkn, v, (int)
+					AppleDataBox.FlagType.ContainsData);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "trkn" data box.
+		/// </remarks>
+		public override uint TrackCount {
+			get {
+				foreach (AppleDataBox box in DataBoxes (BoxType.Trkn))
+					if (box.Flags == (int)
+						AppleDataBox.FlagType.ContainsData &&
+						box.Data.Count >= 6)
+						return box.Data.Mid (4, 2).ToUShort ();
+				
+				return 0;
+			}
+			set {
+				uint track = Track;
+				if (value == 0 && track == 0) {
+					ClearData (BoxType.Trkn);
+					return;
+				}
+				
+				ByteVector v = ByteVector.FromUShort (0);
+				v.Add (ByteVector.FromUShort ((ushort) track));
+				v.Add (ByteVector.FromUShort ((ushort) value));
+				v.Add (ByteVector.FromUShort (0));
+				SetData (BoxType.Trkn, v, (int)
+					AppleDataBox.FlagType.ContainsData);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "disk" data box.
+		/// </remarks>
+		public override uint Disc {
+			get {
+				foreach (AppleDataBox box in DataBoxes (BoxType.Disk))
+					if (box.Flags == (int)
+						AppleDataBox.FlagType.ContainsData &&
+						box.Data.Count >= 4)
+						return box.Data.Mid (2, 2).ToUShort ();
+				
+				return 0;
+			}
+			set {
+				uint count = DiscCount;
+				if (value == 0 && count == 0) {
+					ClearData (BoxType.Disk);
+					return;
+				}
+				
+				ByteVector v = ByteVector.FromUShort (0);
+				v.Add (ByteVector.FromUShort ((ushort) value));
+				v.Add (ByteVector.FromUShort ((ushort) count));
+				v.Add (ByteVector.FromUShort (0));
+				
+				SetData (BoxType.Disk, v, (int)
+					AppleDataBox.FlagType.ContainsData);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "disk" data box.
+		/// </remarks>
+		public override uint DiscCount {
+			get {
+				foreach (AppleDataBox box in DataBoxes (BoxType.Disk))
+					if (box.Flags == (int)
+						AppleDataBox.FlagType.ContainsData &&
+						box.Data.Count >= 6)
+						return box.Data.Mid (4, 2).ToUShort ();
+				
+				return 0;
+			}
+			set {
+				uint disc = Disc;
+				if (value == 0 && disc == 0) {
+					ClearData (BoxType.Disk);
+					return;
+				}
+				
+				ByteVector v = ByteVector.FromUShort (0);
+				v.Add (ByteVector.FromUShort ((ushort) disc));
+				v.Add (ByteVector.FromUShort ((ushort) value));
+				v.Add (ByteVector.FromUShort (0));
+				SetData (BoxType.Disk, v, (int)
+					AppleDataBox.FlagType.ContainsData);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@lyr" data box.
+		/// </remarks>
+		public override string Lyrics {
+			get {
+				foreach (AppleDataBox box in DataBoxes (BoxType.Lyr))
+					return box.Text;
+				return null;
+			}
+			set {
+				SetText (BoxType.Lyr, value);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "@grp" data box.
+		/// </remarks>
+		public override string Grouping {
+			get {
+				foreach (AppleDataBox box in DataBoxes(BoxType.Grp))
+					return box.Text;
+				
+				return null;
+			}
+			set {SetText(BoxType.Grp, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "tmpo" data box.
+		/// </remarks>
+		public override uint BeatsPerMinute {
+			get {
+				foreach (AppleDataBox box in DataBoxes (BoxType.Tmpo))
+					if (box.Flags == (uint)
+						AppleDataBox.FlagType.ForTempo)
+						return box.Data.ToUInt ();
+				
+				return 0;
+			}
+			set {
+				if (value == 0) {
+					ClearData (BoxType.Tmpo);
+					return;
+				}
+				
+				SetData (BoxType.Tmpo,
+					ByteVector.FromUShort ((ushort)value),
+					(uint) AppleDataBox.FlagType.ForTempo);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "cond" data box.
+		/// </remarks>
+		public override string Conductor {
+			get {
+				foreach (AppleDataBox box in DataBoxes(BoxType.Cond))
+					return box.Text;
+				
+				return null;
+			}
+			set {SetText(BoxType.Cond, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "cprt" data box.
+		/// </remarks>
+		public override string Copyright {
+			get {
+				foreach (AppleDataBox box in DataBoxes(BoxType.Cprt))
+					return box.Text;
+				
+				return null;
+			}
+			set {SetText(BoxType.Cprt, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the band or artist who
+		///    is credited in the creation of the entire album or
+		///    collection containing the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the band or artist who is credited in the creation
+		///    of the entire album or collection containing the media
+		///    described by the current instance or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "soaa"
+		///    Box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		///    http://code.google.com/p/mp4v2/wiki/iTunesMetadata
+		/// </remarks>
+		public override string [] AlbumArtistsSort {
+			get {return GetText (BoxType.Soaa);}
+			set {SetText (BoxType.Soaa, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "soar" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		///    http://code.google.com/p/mp4v2/wiki/iTunesMetadata
+		/// </remarks>
+		public override string[] PerformersSort {
+			get {return GetText (BoxType.Soar);}
+			set {SetText (BoxType.Soar, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the Composer credited
+		///    in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the Composers in the media described by the current instance,
+		///    or an empty array if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "soar" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		///    http://code.google.com/p/mp4v2/wiki/iTunesMetadata
+		/// </remarks>
+		public override string[] ComposersSort {
+			get {return GetText (BoxType.Soco);}
+			set {SetText (BoxType.Soco, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the Album Title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names for
+		///    the Album Title in the media described by the current
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "soal" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		///    http://code.google.com/p/mp4v2/wiki/iTunesMetadata
+		/// </remarks>
+		public override string AlbumSort {
+			get {
+				string [] text = GetText (BoxType.Soal);
+				return text.Length == 0 ? null : text [0];
+			}
+			set {SetText (BoxType.Soal, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the Track Title in the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names for
+		///    the Track Title in the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "sonm" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		///    http://code.google.com/p/mp4v2/wiki/iTunesMetadata
+		/// </remarks>
+		public override string TitleSort {
+			get {
+				string [] text = GetText (BoxType.Sonm);
+				return text.Length == 0 ? null : text [0];
+			}
+			set {SetText (BoxType.Sonm, value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ArtistID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ArtistID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzArtistId {
+			get {return GetDashBox("com.apple.iTunes","MusicBrainz Artist Id");}
+			set {SetDashBox("com.apple.iTunes", "MusicBrainz Artist Id", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseId {
+			get {return GetDashBox("com.apple.iTunes","MusicBrainz Album Id");}
+			set {SetDashBox("com.apple.iTunes", "MusicBrainz Album Id",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseArtistID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseArtistID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseArtistId {
+			get {return GetDashBox("com.apple.iTunes","MusicBrainz Album Artist Id");}
+			set {SetDashBox("com.apple.iTunes", "MusicBrainz Album Artist Id",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz TrackID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    TrackID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzTrackId {
+		    get { return GetDashBox("com.apple.iTunes","MusicIP PUID");}
+		    set {SetDashBox("com.apple.iTunes", "MusicIP PUID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz DiscID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    DiscID for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzDiscId {
+			get {return GetDashBox("com.apple.iTunes","MusicBrainz Disc Id");}
+			set {SetDashBox("com.apple.iTunes", "MusicBrainz Disc Id",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicIP Puid
+		///    for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicIpId {
+			get {return GetDashBox("com.apple.iTunes","MusicIP PUID");}
+			set {SetDashBox("com.apple.iTunes", "MusicIP PUID",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the AmazonID
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the AmazonID
+		///    for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string AmazonId {
+			get {return GetDashBox("com.apple.iTunes","ASIN");}
+			set {SetDashBox("com.apple.iTunes", "ASIN",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseStatus
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseStatus for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseStatus {
+			get {return GetDashBox("com.apple.iTunes","MusicBrainz Album Status");}
+			set {SetDashBox("com.apple.iTunes", "MusicBrainz Album Status",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz ReleaseType
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseType for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseType {
+			get {return GetDashBox("com.apple.iTunes","MusicBrainz Album Type");}
+			set {SetDashBox("com.apple.iTunes", "MusicBrainz Album Type",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Country
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseCountry for the media described by the current 
+		///    instance, or null if no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "dash"/"----" box type.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string MusicBrainzReleaseCountry {
+			get {return GetDashBox("com.apple.iTunes","MusicBrainz Album Release Country");}
+			set {SetDashBox("com.apple.iTunes", "MusicBrainz Album Release Country",value);}
+		}
+
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "covr" data box.
+		/// </remarks>
+		public override IPicture [] Pictures {
+			get {
+				List<Picture> l = new List<Picture> ();
+				
+				foreach (AppleDataBox box in DataBoxes(BoxType.Covr)) {
+					Picture p = new Picture (box.Data);
+					p.Type = PictureType.FrontCover;
+					l.Add (p);
+				}
+				
+				return (Picture []) l.ToArray ();
+			}
+			set {
+				if (value == null || value.Length == 0) {
+					ClearData (BoxType.Covr);
+					return;
+				}
+				
+				AppleDataBox [] boxes =
+					new AppleDataBox [value.Length];
+				for (int i = 0; i < value.Length; i ++) {
+					uint type = (uint)
+						AppleDataBox.FlagType.ContainsData;
+					
+					if (value [i].MimeType == "image/jpeg")
+						type = (uint)
+							AppleDataBox.FlagType.ContainsJpegData;
+					else if (value [i].MimeType == "image/png")
+						type = (uint)
+							AppleDataBox.FlagType.ContainsPngData;
+					
+					boxes [i] = new AppleDataBox (value [i].Data, type);
+				}
+				
+				SetData(BoxType.Covr, boxes);
+			}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance does not
+		///    any values. Otherwise <see langword="false" />.
+		/// </value>
+		public override bool IsEmpty {
+			get {return !ilst_box.HasChildren;}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			ilst_box.ClearChildren ();
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Box.cs b/lib/TagLib/TagLib/Mpeg4/Box.cs
new file mode 100644
index 0000000..7cca278
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Box.cs
@@ -0,0 +1,592 @@
+//
+// Box.cs: Provides a generic implementation of a ISO/IEC 14496-12 box.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This abstract class provides a generic implementation of a
+	///    ISO/IEC 14496-12 box.
+	/// </summary>
+	public class Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the box header.
+		/// </summary>
+		private BoxHeader header;
+		
+		/// <summary>
+		///    Contains the box's handler, if applicable.
+		/// </summary>
+		private IsoHandlerBox handler;
+		
+		/// <summary>
+		///    Contains the position of the box data.
+		/// </summary>
+		private long data_position;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Box" /> with a specified header and handler.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object describing the new
+		///    instance.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance, or <see
+		///    langword="null" /> if no handler applies.
+		/// </param>
+		protected Box (BoxHeader header, IsoHandlerBox handler)
+		{
+			this.header = header;
+			this.data_position = header.Position + header.HeaderSize;
+			this.handler = handler;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Box" /> with a specified header.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object describing the new
+		///    instance.
+		/// </param>
+		protected Box (BoxHeader header) : this (header, null)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Box" /> with a specified box type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the box
+		///    type to use for the new instance.
+		/// </param>
+		protected Box (ByteVector type) : this (new BoxHeader (type))
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the MPEG-4 box type of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the four
+		///    byte box type of the current instance.
+		/// </value>
+		public virtual ByteVector BoxType {
+			get {return header.BoxType;}
+		}
+		
+		/// <summary>
+		///    Gets the total size of the current instance as it last
+		///    appeared on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the total size of
+		///    the current instance as it last appeared on disk.
+		/// </value>
+		public virtual int Size {
+			get {return (int)header.TotalBoxSize;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    contained in the current instance.
+		/// </value>
+		public virtual ByteVector Data {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public virtual IEnumerable<Box> Children {
+			get {return null;}
+		}
+		
+		/// <summary>
+		///    Gets the handler box that applies to the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the current instance, or <see
+		///    langword="null" /> if no handler applies.
+		/// </value>
+		public IsoHandlerBox Handler {
+			get {return handler;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance, including its children, to
+		///    a new <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			return Render (new ByteVector ());
+		}
+		
+		/// <summary>
+		///    Gets a child box from the current instance by finding
+		///    a matching box type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the box
+		///    type to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Box" /> object containing the matched box,
+		///    or <see langword="null" /> if no matching box was found.
+		/// </returns>
+		public Box GetChild (ByteVector type)
+		{
+			if (Children == null)
+				return null;
+			
+			foreach (Box box in Children)
+				if (box.BoxType == type)
+					return box;
+			
+			return null;
+		}
+		
+		/*
+		/// <summary>
+		///    Gets a child box from the current instance by finding
+		///    a matching object type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="System.Type" /> object containing the object
+		///    type to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Box" /> object containing the matched box,
+		///    or <see langword="null" /> if no matching box was found.
+		/// </returns>
+		public Box GetChild (System.Type type)
+		{
+			if (Children == null)
+				return null;
+			
+			foreach (Box box in Children)
+				if (box.GetType () == type)
+					return box;
+			
+			return null;
+		}
+		*/
+		
+		/// <summary>
+		///    Gets a child box from the current instance by finding
+		///    a matching box type, searching recursively.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the box
+		///    type to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Box" /> object containing the matched box,
+		///    or <see langword="null" /> if no matching box was found.
+		/// </returns>
+		public Box GetChildRecursively (ByteVector type)
+		{
+			if (Children == null)
+				return null;
+			
+			foreach (Box box in Children)
+				if (box.BoxType == type)
+					return box;
+			
+			foreach (Box box in Children) {
+				Box child_box = box.GetChildRecursively (type);
+				if (child_box != null)
+					return child_box;
+			}
+			
+			return null;
+		}
+		
+		/*
+		/// <summary>
+		///    Gets a child box from the current instance by finding
+		///    a matching object type, searching recursively.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="System.Type" /> object containing the object
+		///    type to match.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Box" /> object containing the matched box,
+		///    or <see langword="null" /> if no matching box was found.
+		/// </returns>
+		public Box GetChildRecursively (System.Type type)
+		{
+			if (Children == null)
+				return null;
+			
+			foreach (Box box in Children)
+				if (box.GetType () == type)
+					return box;
+			
+			foreach (Box box in Children) {
+				Box child_box = box.GetChildRecursively (type);
+				if (child_box != null)
+					return child_box;
+			}
+			
+			return null;
+		}
+		*/
+		
+		/// <summary>
+		///    Removes all children with a specified box type from the
+		///    current instance.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the box
+		///    type to remove.
+		/// </param>
+		public void RemoveChild (ByteVector type)
+		{
+			ICollection<Box> children = Children as ICollection<Box>;
+			
+			if (children == null)
+				return;
+			
+			foreach (Box b in new List<Box> (children))
+				if (b.BoxType == type)
+					children.Remove (b);
+		}
+		
+		/*
+		/// <summary>
+		///    Removes all children with a specified box type from the
+		///    current instance.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the box
+		///    type to remove.
+		/// </param>
+		public void RemoveChild (System.Type type)
+		{
+			ICollection<Box> children = Children as ICollection<Box>;
+			
+			if (children == null)
+				return;
+			
+			foreach (Box b in new List<Box> (children))
+				if (b.GetType () == type)
+					children.Remove (b);
+		}
+		*/
+		
+		/// <summary>
+		///    Removes a specified box from the current instance.
+		/// </summary>
+		/// <param name="box">
+		///    A <see cref="Box" /> object to remove from the current
+		///    instance.
+		/// </param>
+		public void RemoveChild (Box box)
+		{
+			ICollection<Box> children = Children as ICollection<Box>;
+			
+			if (children != null)
+				children.Remove (box);
+		}
+		
+		/// <summary>
+		///    Adds a specified box to the current instance.
+		/// </summary>
+		/// <param name="box">
+		///    A <see cref="Box" /> object to add to the current
+		///    instance.
+		/// </param>
+		public void AddChild (Box box)
+		{
+			ICollection<Box> children = Children as ICollection<Box>;
+			
+			if (children != null)
+				children.Add (box);
+		}
+		
+		/// <summary>
+		///    Removes all children from the current instance.
+		/// </summary>
+		public void ClearChildren ()
+		{
+			ICollection<Box> children = Children as ICollection<Box>;
+			
+			if (children != null)
+				children.Clear ();
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance has children.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance has any children.
+		/// </value>
+		public bool HasChildren {
+			get {
+				ICollection<Box> children =
+					Children as ICollection<Box>;
+				
+				return children != null && children.Count > 0;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Properties
+		
+		/// <summary>
+		///    Gets the size of the data contained in the current
+		///    instance, minux the size of any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the size of
+		///    the data contained in the current instance.
+		/// </value>
+		protected int DataSize {
+			get {return (int)(header.DataSize + data_position -
+				DataPosition);}
+		}
+		
+		/// <summary>
+		///    Gets the position of the data contained in the current
+		///    instance, after any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position of
+		///    the data contained in the current instance.
+		/// </value>
+		protected virtual long DataPosition {
+			get {return data_position;}
+		}
+		
+		/// <summary>
+		///    Gets the header of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    of the current instance.
+		/// </value>
+		protected BoxHeader Header {
+			get {return header;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Loads the children of the current instance from a
+		///    specified file using the internal data position and size.
+		/// </summary>
+		/// <param name="file">
+		///    The <see cref="TagLib.File" /> from which the current
+		///    instance was read and from which to read the children.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    boxes read from the file.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		protected IEnumerable<Box> LoadChildren (TagLib.File file)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			List<Box> children = new List<Box> ();
+			
+			long position = DataPosition;
+			long end = position + DataSize;
+			
+			header.Box = this;
+			while (position < end) {
+				Box child = BoxFactory.CreateBox (file,
+					position, header, handler,
+					children.Count);
+				children.Add (child);
+				position += child.Size;
+			}
+			header.Box = null;
+			
+			return children;
+		}
+		
+		/// <summary>
+		///    Loads the data of the current instance from a specified
+		///    file using the internal data position and size.
+		/// </summary>
+		/// <param name="file">
+		///    The <see cref="TagLib.File" /> from which the current
+		///    instance was read and from which to read the data.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    read from the file.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		protected ByteVector LoadData (TagLib.File file)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Seek (DataPosition);
+			return file.ReadBlock (DataSize);
+		}
+		
+		/// <summary>
+		///    Renders the current instance, including its children, to
+		///    a new <see cref="ByteVector" /> object, preceeding the
+		///    contents with a specified block of data.
+		/// </summary>
+		/// <param name="topData">
+		///    A <see cref="ByteVector" /> object containing box
+		///    specific header data to preceed the content.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		protected virtual ByteVector Render (ByteVector topData)
+		{
+			bool free_found = false;
+			ByteVector output = new ByteVector ();
+			
+			if (Children != null)
+				foreach (Box box in Children)
+					if (box.GetType () == typeof (
+						IsoFreeSpaceBox))
+						free_found = true;
+					else
+						output.Add (box.Render ());
+			else if (Data != null)
+				output.Add (Data);
+			
+			// If there was a free, don't take it away, and let meta
+			// be a special case.
+			if (free_found || BoxType == Mpeg4.BoxType.Meta) {
+				long size_difference = DataSize - output.Count;
+				
+				// If we have room for free space, add it so we
+				// don't have to resize the file.
+				if (header.DataSize != 0 && size_difference >= 8)
+					output.Add ((new IsoFreeSpaceBox (
+						size_difference)).Render ());
+				
+				// If we're getting bigger, get a lot bigger so
+				// we might not have to again.
+				else
+					output.Add ((new IsoFreeSpaceBox (2048
+						)).Render ());
+			}
+			
+			// Adjust the header's data size to match the content.
+			header.DataSize = topData.Count + output.Count;
+			
+			// Render the full box.
+			output.Insert (0, topData);
+			output.Insert (0, header.Render ());
+			
+			return output;
+		}
+		
+		#endregion
+      
+		/*
+		#region Internal Methods
+		
+		/// <summary>
+		///    Dumps the child tree of the current instance to the
+		///    console.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="string" /> object to preface each line with.
+		/// </param>
+		internal void DumpTree (string start)
+		{
+			if (BoxType == BoxType.Data)
+			Console.WriteLine ("{0}{1} {2}", start,
+				BoxType.ToString (),
+				(this as AppleDataBox).Text);
+			else
+				Console.WriteLine ("{0}{1}", start,
+				BoxType.ToString ());
+			
+			if (Children != null)
+				foreach (Box child in Children)
+					child.DumpTree (start + "   ");
+		}
+		
+		#endregion
+		*/
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/BoxFactory.cs b/lib/TagLib/TagLib/Mpeg4/BoxFactory.cs
new file mode 100644
index 0000000..b28dc57
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/BoxFactory.cs
@@ -0,0 +1,265 @@
+//
+// BoxFactory.cs: Provides support for reading boxes from a file.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This static class provides support for reading boxes from a file.
+	/// </summary>
+	public static class BoxFactory
+	{
+		/// <summary>
+		///    Creates a box by reading it from a file given its header,
+		///    parent header, handler, and index in its parent.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    to read from.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    of the box to create.
+		/// </param>
+		/// <param name="parent">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    of the parent box.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new box.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value containing the index of the
+		///    new box in its parent.
+		/// </param>
+		/// <returns>
+		///    A newly created <see cref="Box" /> object.
+		/// </returns>
+		private static Box CreateBox (TagLib.File file,
+		                              BoxHeader header,
+		                              BoxHeader parent,
+		                              IsoHandlerBox handler,
+		                              int index)
+		{
+			// The first few children of an "stsd" are sample
+			// entries.
+			if (parent.BoxType == BoxType.Stsd &&
+				parent.Box is IsoSampleDescriptionBox &&
+				index < (parent.Box as IsoSampleDescriptionBox).EntryCount) {
+				if (handler != null &&
+					handler.HandlerType == BoxType.Soun)
+					return new IsoAudioSampleEntry (header,
+						file, handler);
+				else if (handler != null && handler.HandlerType == BoxType.Vide)
+					return new IsoVisualSampleEntry (header,
+						file, handler);
+				else if (handler != null && handler.HandlerType == BoxType.Alis)
+					return new IsoAudioSampleEntry (header,
+						file, handler);
+				else
+					return new IsoSampleEntry (header,
+						file, handler);
+			}
+			
+			// Standard items...
+			ByteVector type = header.BoxType;
+			
+			if (type == BoxType.Mvhd)
+				return new IsoMovieHeaderBox (header, file,
+					handler);
+			else if (type == BoxType.Stbl)
+				return new IsoSampleTableBox (header, file,
+					handler);
+			else if (type == BoxType.Stsd)
+				return new IsoSampleDescriptionBox (header,
+					file, handler);
+			else if (type == BoxType.Stco)
+				return new IsoChunkOffsetBox (header, file,
+					handler);
+			else if (type == BoxType.Co64)
+				return new IsoChunkLargeOffsetBox (header, file,
+					handler);
+			else if (type == BoxType.Hdlr)
+				return new IsoHandlerBox (header, file,
+					handler);
+			else if (type == BoxType.Udta)
+				return new IsoUserDataBox (header, file,
+					handler);
+			else if (type == BoxType.Meta)
+				return new IsoMetaBox (header, file, handler);
+			else if (type == BoxType.Ilst)
+				return new AppleItemListBox (header, file,
+					handler);
+			else if (type == BoxType.Data)
+				return new AppleDataBox (header, file, handler);
+			else if (type == BoxType.Esds)
+				return new AppleElementaryStreamDescriptor (
+					header, file, handler);
+			else if (type == BoxType.Free || type == BoxType.Skip)
+				return new IsoFreeSpaceBox (header, file,
+					handler);
+			else if (type == BoxType.Mean || type == BoxType.Name)
+				return new AppleAdditionalInfoBox (header, file,
+					handler);
+			
+			// If we still don't have a tag, and we're inside an
+			// ItemListBox, load the box as an AnnotationBox
+			// (Apple tag item).
+			if (parent.BoxType == BoxType.Ilst)
+				return new AppleAnnotationBox (header, file,
+					handler);
+			
+			// Nothing good. Go generic.
+			return new UnknownBox (header, file, handler);
+		}
+		
+		/// <summary>
+		///    Creates a box by reading it from a file given its
+		///    position in the file, parent header, handler, and index
+		///    in its parent.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    to read from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying at what seek
+		///    position in <paramref name="file" /> to start reading.
+		/// </param>
+		/// <param name="parent">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    of the parent box.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new box.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value containing the index of the
+		///    new box in its parent.
+		/// </param>
+		/// <returns>
+		///    A newly created <see cref="Box" /> object.
+		/// </returns>
+		internal static Box CreateBox (TagLib.File file, long position,
+		                               BoxHeader parent,
+		                               IsoHandlerBox handler, int index)
+		{
+			BoxHeader header = new BoxHeader (file, position);
+			return CreateBox (file, header, parent, handler, index);
+		}
+		
+		/// <summary>
+		///    Creates a box by reading it from a file given its
+		///    position in the file and handler.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    to read from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying at what seek
+		///    position in <paramref name="file" /> to start reading.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new box.
+		/// </param>
+		/// <returns>
+		///    A newly created <see cref="Box" /> object.
+		/// </returns>
+		public static Box CreateBox (TagLib.File file, long position,
+		                             IsoHandlerBox handler)
+		{
+			return CreateBox (file, position, BoxHeader.Empty,
+				handler, -1);
+		}
+		
+		/// <summary>
+		///    Creates a box by reading it from a file given its
+		///    position in the file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    to read from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifying at what seek
+		///    position in <paramref name="file" /> to start reading.
+		/// </param>
+		/// <returns>
+		///    A newly created <see cref="Box" /> object.
+		/// </returns>
+		public static Box CreateBox (TagLib.File file, long position)
+		{
+			return CreateBox (file, position, null);
+		}
+		
+		/// <summary>
+		///    Creates a box by reading it from a file given its header
+		///    and handler.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    to read from.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    of the box to create.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new box.
+		/// </param>
+		/// <returns>
+		///    A newly created <see cref="Box" /> object.
+		/// </returns>
+		public static Box CreateBox (TagLib.File file, BoxHeader header,
+		                             IsoHandlerBox handler)
+		{
+			return CreateBox (file, header, BoxHeader.Empty,
+				handler, -1);
+		}
+		
+		/// <summary>
+		///    Creates a box by reading it from a file given its header
+		///    and handler.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    to read from.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    of the box to create.
+		/// </param>
+		/// <returns>
+		///    A newly created <see cref="Box" /> object.
+		/// </returns>
+		public static Box CreateBox (TagLib.File file, BoxHeader header)
+		{
+			return CreateBox (file, header, null);
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/BoxHeader.cs b/lib/TagLib/TagLib/Mpeg4/BoxHeader.cs
new file mode 100644
index 0000000..aa344b3
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/BoxHeader.cs
@@ -0,0 +1,424 @@
+//
+// BoxHeader.cs: Provides support for reading and writing headers for ISO/IEC
+// 14496-12 boxes.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This structure provides support for reading and writing headers
+	///    for ISO/IEC 14496-12 boxes.
+	/// </summary>
+	public struct BoxHeader
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the box type.
+		/// </summary>
+		private ByteVector box_type;
+		
+		/// <summary>
+		///    Contains the extended type.
+		/// </summary>
+		private ByteVector extended_type;
+		
+		/// <summary>
+		///    Contains the box size.
+		/// </summary>
+		private ulong box_size;
+		
+		/// <summary>
+		///    Contains the header size.
+		/// </summary>
+		private uint header_size;
+		
+		/// <summary>
+		///    Contains the position of the header.
+		/// </summary>
+		private long position;
+		
+		/// <summary>
+		///    Contains the box (temporarily).
+		/// </summary>
+		private Box box;
+		
+		/// <summary>
+		///    Indicated that the header was read from a file.
+		/// </summary>
+		private bool from_disk;
+		
+		#endregion
+		
+		
+		
+		#region Public Fields
+		
+		/// <summary>
+		///    An empty box header.
+		/// </summary>
+		public static readonly BoxHeader Empty = new BoxHeader ("xxxx");
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="BoxHeader" /> by reading it from a specified seek
+		///    position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the new
+		///    instance from.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specifiying the seek position
+		///    in <paramref name="file" /> at which to start reading.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    There isn't enough data in the file to read the complete
+		///    header.
+		/// </exception>
+		public BoxHeader (TagLib.File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			this.box = null;
+			this.from_disk = true;
+			this.position = position;
+			file.Seek (position);
+			
+			ByteVector data = file.ReadBlock (32);
+			int offset = 0;
+			
+			if (data.Count < 8 + offset)
+				throw new CorruptFileException (
+					"Not enough data in box header.");
+			
+			header_size = 8;
+			box_size = data.Mid (offset, 4).ToUInt ();
+			box_type = data.Mid (offset + 4, 4);
+			
+			// If the size is 1, that just tells us we have a
+			// massive ULONG size waiting for us in the next 8
+			// bytes.
+			if (box_size == 1) {
+				if (data.Count < 8 + offset)
+					throw new CorruptFileException (
+						"Not enough data in box header.");
+				
+				header_size += 8;
+				box_size = data.Mid (offset, 8).ToULong ();
+				offset += 8;
+			}
+			
+			// UUID has a special header with 16 extra bytes.
+			if (box_type == Mpeg4.BoxType.Uuid) {
+				if (data.Count < 16 + offset)
+					throw new CorruptFileException (
+						"Not enough data in box header.");
+				
+				header_size += 16;
+				extended_type = data.Mid (offset, 16);
+			} else
+				extended_type = null;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="BoxHeader" /> with a specified box type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the four
+		///    byte box type.
+		/// </param>
+		/// <remarks>
+		///    <see cref="BoxHeader(ByteVector,ByteVector)" /> must be
+		///    used to create a header of type "<c>uuid</c>".
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="type" /> is <see langword="null" /> or is
+		///    equal to "<c>uuid</c>".
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="type" /> isn't exactly 4 bytes long.
+		/// </exception>
+		public BoxHeader (ByteVector type) : this (type, null)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="BoxHeader" /> with a specified box type and
+		///    optionally extended type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the four
+		///    byte box type.
+		/// </param>
+		/// <param name="extendedType">
+		///    A <see cref="ByteVector" /> object containing the four
+		///    byte box type.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="type" /> is <see langword="null" /> - or -
+		///    <paramref name="type" /> is equal to "<c>uuid</c>" and
+		///    <paramref name="extendedType" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="type" /> isn't exactly 4 bytes long - or
+		///    - <paramref name="type" /> isn't "<c>uuid</c>" but
+		///    <paramref name="extendedType" /> isn't <see
+		///    langword="null" /> - or - paramref name="type" /> is
+		///    "<c>uuid</c>" but <paramref name="extendedType" /> isn't
+		///    exactly 16 bytes long.
+		/// </exception>
+		public BoxHeader (ByteVector type, ByteVector extendedType)
+		{
+			position = -1;
+			box = null;
+			from_disk = false;
+			box_type = type;
+			
+			if (type == null)
+				throw new ArgumentNullException ("type");
+			
+			if (type.Count != 4)
+				throw new ArgumentException (
+					"Box type must be 4 bytes in length.",
+					"type");
+			
+			box_size = header_size = 8;
+			
+			if (type != "uuid") {
+				if (extendedType != null)
+					throw new ArgumentException (
+						"Extended type only permitted for 'uuid'.",
+						"extendedType");
+				
+				this.extended_type = extendedType;
+				return;
+			}
+			
+			if (extendedType == null)
+				throw new ArgumentNullException ("extendedType");
+			
+			if (extendedType.Count != 16)
+				throw new ArgumentException (
+					"Extended type must be 16 bytes in length.",
+					"extendedType");
+			
+			box_size = header_size = 24;
+			this.extended_type = extendedType;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the type of box represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the 4 byte
+		///    box type.
+		/// </value>
+		public ByteVector BoxType {
+			get {return box_type;}
+		}
+		
+		/// <summary>
+		///    Gets the extended type of the box represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the 16 byte
+		///    extended type, or <see langword="null" /> if <see
+		///    cref="BoxType" /> is not "<c>uuid</c>".
+		/// </value>
+		public ByteVector ExtendedType {
+			get {return extended_type;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the header represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the size of the
+		///    header represented by the current instance.
+		/// </value>
+		public long HeaderSize {
+			get {return header_size;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the size of the data in the box described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the size of the
+		///    data in the box described by the current instance.
+		/// </value>
+		public long DataSize {
+			get {return (long) (box_size - header_size);}
+			set {box_size = (ulong) value + header_size;}
+		}
+		
+		/// <summary>
+		///    Gets the offset of the box data from the position of the
+		///    header.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the offset of the
+		///    box data from the position of the header.
+		/// </value>
+		[Obsolete("Use HeaderSize")]
+		public long DataOffset {
+			get {return header_size;}
+		}
+		
+		/// <summary>
+		///    Gets the total size of the box described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the total size of
+		///    the box described by the current instance.
+		/// </value>
+		public long TotalBoxSize {
+			get {return (long)box_size;}
+		}
+		
+		/// <summary>
+		///    Gets the position box represented by the current instance
+		///    in the file it comes from.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position box
+		///    represented by the current instance in the file it comes
+		///    from.
+		/// </value>
+		public long Position {
+			get {return from_disk ? position : -1;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Overwrites the header on disk, updating it to include a
+		///    change in the size of the box.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    from which the box originates.
+		/// </param>
+		/// <param name="sizeChange">
+		///    A <see cref="long" /> value indicating the change in the
+		///    size of the box described by the current instance.
+		/// </param>
+		/// <returns>
+		///    The size change encountered by the box that parents the
+		///    box described the the current instance, equal to the
+		///    size change of the box plus any size change that should
+		///    happen in the header.
+		/// </returns>
+		public long Overwrite (TagLib.File file, long sizeChange)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (!from_disk)
+				throw new InvalidOperationException (
+					"Cannot overwrite headers not on disk.");
+			
+			long old_header_size = HeaderSize;
+			DataSize += sizeChange;
+			file.Insert (Render (), position, old_header_size);
+			return sizeChange + HeaderSize - old_header_size;
+		}
+		
+		/// <summary>
+		///    Renders the header represented by the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			// Enlarge for size if necessary.
+			if ((header_size == 8 || header_size == 24) &&
+				box_size > uint.MaxValue) {
+				header_size += 8;
+				box_size += 8;
+			}
+			
+			// Add the box size and type to the output.
+			ByteVector output = ByteVector.FromUInt (
+				(header_size == 8 || header_size == 24) ?
+				(uint) box_size : 1);
+			output.Add (box_type);
+			
+			// If the box size is 16 or 32, we must have more a
+			// large header to append.
+			if (header_size == 16 || header_size == 32)
+				output.Add (ByteVector.FromULong (box_size));
+			
+			// The only reason for such a big size is an extended
+			// type. Extend!!!
+			if (header_size >= 24)
+				output.Add (extended_type);
+			
+			return output;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Internal Properties
+		
+		/// <summary>
+		///    Gets and sets the box represented by the current instance
+		///    as a means of temporary storage for internal uses.
+		/// </summary>
+		internal Box Box {get {return box;} set {box = value;}}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/BoxTypes.cs b/lib/TagLib/TagLib/Mpeg4/BoxTypes.cs
new file mode 100644
index 0000000..3d83f4a
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/BoxTypes.cs
@@ -0,0 +1,95 @@
+//
+// BoxTypes.cs: Contains common box names.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    <see cref="BoxType" /> provides references to different box types
+	///    used by the library.
+	/// </summary>
+	/// <remarks>
+	///    <para>This class is used to severely reduce the number of times
+	///    these types are created in <see cref="AppleTag" />, greatly
+	///    improving the speed at which warm files are read.</para>
+	///    <para>The reason it is marked as internal is because I'm not sure
+	///    I like the way the fields are named, and it is really
+	///    unneccessary for external uses. While the library may use
+	///    <c>DataBoxes (BoxType.Gen, BoxType.Gnre);</c>, an external user
+	///    could use <c>tag.DataBoxes ("gen", "gnre");</c> with the same
+	///    result.</para>
+	/// </remarks>
+	internal static class BoxType
+	{
+		public static readonly ReadOnlyByteVector Aart = "aART";
+		public static readonly ReadOnlyByteVector Alb  = AppleTag.FixId ("alb");
+		public static readonly ReadOnlyByteVector Art  = AppleTag.FixId ("ART");
+		public static readonly ReadOnlyByteVector Cmt  = AppleTag.FixId ("cmt");
+		public static readonly ReadOnlyByteVector Cond = "cond";
+		public static readonly ReadOnlyByteVector Covr = "covr";
+		public static readonly ReadOnlyByteVector Co64 = "co64";
+		public static readonly ReadOnlyByteVector Cpil = "cpil";
+		public static readonly ReadOnlyByteVector Cprt = "cprt";
+		public static readonly ReadOnlyByteVector Data = "data";
+		public static readonly ReadOnlyByteVector Day  = AppleTag.FixId ("day");
+		public static readonly ReadOnlyByteVector Disk = "disk";
+		public static readonly ReadOnlyByteVector Esds = "esds";
+		public static readonly ReadOnlyByteVector Ilst = "ilst";
+		public static readonly ReadOnlyByteVector Free = "free";
+		public static readonly ReadOnlyByteVector Gen  = AppleTag.FixId ("gen");
+		public static readonly ReadOnlyByteVector Gnre = "gnre";
+		public static readonly ReadOnlyByteVector Grp  = AppleTag.FixId("grp");
+		public static readonly ReadOnlyByteVector Hdlr = "hdlr";
+		public static readonly ReadOnlyByteVector Lyr  = AppleTag.FixId ("lyr");
+		public static readonly ReadOnlyByteVector Mdat = "mdat";
+		public static readonly ReadOnlyByteVector Mdia = "mdia";
+		public static readonly ReadOnlyByteVector Meta = "meta";
+		public static readonly ReadOnlyByteVector Mean = "mean";
+		public static readonly ReadOnlyByteVector Minf = "minf";
+		public static readonly ReadOnlyByteVector Moov = "moov";
+		public static readonly ReadOnlyByteVector Mvhd = "mvhd";
+		public static readonly ReadOnlyByteVector Nam  = AppleTag.FixId ("nam");
+		public static readonly ReadOnlyByteVector Name = "name";
+		public static readonly ReadOnlyByteVector Skip = "skip";
+		public static readonly ReadOnlyByteVector Soaa = "soaa"; // Album Artist Sort
+		public static readonly ReadOnlyByteVector Soar = "soar"; // Performer Sort
+		public static readonly ReadOnlyByteVector Soco = "soco"; // Composer Sort
+		public static readonly ReadOnlyByteVector Sonm = "sonm"; // Track Title Sort
+		public static readonly ReadOnlyByteVector Soal = "soal"; // Album Title Sort
+		public static readonly ReadOnlyByteVector Stbl = "stbl";
+		public static readonly ReadOnlyByteVector Stco = "stco";
+		public static readonly ReadOnlyByteVector Stsd = "stsd";
+		public static readonly ReadOnlyByteVector Tmpo = "tmpo";
+		public static readonly ReadOnlyByteVector Trak = "trak";
+		public static readonly ReadOnlyByteVector Trkn = "trkn";
+		public static readonly ReadOnlyByteVector Udta = "udta";
+		public static readonly ReadOnlyByteVector Uuid = "uuid";
+		public static readonly ReadOnlyByteVector Wrt  = AppleTag.FixId ("wrt");
+		public static readonly ReadOnlyByteVector DASH = "----";
+
+		// Handler types.
+		public static readonly ReadOnlyByteVector Soun = "soun";
+		public static readonly ReadOnlyByteVector Vide = "vide";
+
+		// Another handler type, found in wild in audio file ripped using iTunes
+		public static readonly ReadOnlyByteVector Alis = "alis";
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/AppleAdditionalInfoBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleAdditionalInfoBox.cs
new file mode 100644
index 0000000..cadedda
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleAdditionalInfoBox.cs
@@ -0,0 +1,119 @@
+//
+// AppleAdditionalInfoBox.cs: Provides an implementation of an Apple
+// AdditionalInfoBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of an Apple AdditionalInfoBox.
+	/// </summary>
+	public class AppleAdditionalInfoBox : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the box data.
+		/// </summary>
+		private ByteVector data;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleAdditionalInfoBox" /> with a provided header
+		///    and handler by reading the contents from a specified
+		///    file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public AppleAdditionalInfoBox (BoxHeader header, TagLib.File file, IsoHandlerBox handler) : base (header, file, handler)
+		{
+			Data = file.ReadBlock (DataSize);
+		}
+		
+		/// <summary>
+		/// Constructs and initializes a new instance of <see
+		///    cref="AppleAdditionalInfoBox" /> using specified header, version and flags
+		/// </summary>
+		/// <param name="header"></param>
+		/// <param name="version"></param>
+		/// <param name="flags"></param>
+		public AppleAdditionalInfoBox (ByteVector header, byte version, uint flags) : base (header, version, flags)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    contained in the current instance.
+		/// </value>
+		public override ByteVector Data {
+			get {return data;}
+			set {data = value != null ? value : new ByteVector ();}
+		}
+		
+		/// <summary>
+		///    Gets and sets the text contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the text
+		///    contained in the current instance.
+		/// </value>
+		public string Text {
+			get {return Data.ToString (StringType.Latin1);}
+			set {
+				Data = ByteVector.FromString (value,
+					StringType.Latin1);
+			}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/AppleAnnotationBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleAnnotationBox.cs
new file mode 100644
index 0000000..0255f86
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleAnnotationBox.cs
@@ -0,0 +1,110 @@
+//
+// AppleAnnotationBox.cs: Provides an implementation of an Apple AnnotationBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide an
+	///    implementation of an Apple AnnotationBox.
+	/// </summary>
+	public class AppleAnnotationBox : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private IEnumerable<Box> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleAnnotationBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public AppleAnnotationBox (BoxHeader header, TagLib.File file,
+		                           IsoHandlerBox handler)
+		: base (header, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			children = LoadChildren (file);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleAnnotationBox" /> of specified type with no
+		///    children.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing a 4-byte
+		///    box type.
+		/// </param>
+		public AppleAnnotationBox (ByteVector type) : base (type)
+		{
+			children = new List<Box> ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override IEnumerable<Box> Children {
+			get {return children;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/AppleDataBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleDataBox.cs
new file mode 100644
index 0000000..ff28c07
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleDataBox.cs
@@ -0,0 +1,207 @@
+//
+// AppleDataBox.cs: Provides an implementation of an Apple DataBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of an Apple DataBox.
+	/// </summary>
+	public class AppleDataBox : FullBox
+	{
+		#region Enums
+		
+		/// <summary>
+		///    Specifies the type of data contained in a box.
+		/// </summary>
+		public enum FlagType {
+			/// <summary>
+			///    The box contains UTF-8 text.
+			/// </summary>
+			ContainsText = 0x01,
+			
+			/// <summary>
+			///    The box contains binary data.
+			/// </summary>
+			ContainsData = 0x00,
+			
+			/// <summary>
+			///    The box contains data for a tempo box.
+			/// </summary>
+			ForTempo = 0x15,
+			
+			/// <summary>
+			///    The box contains a raw JPEG image.
+			/// </summary>
+			ContainsJpegData = 0x0D,
+			
+			/// <summary>
+			///    The box contains a raw PNG image.
+			/// </summary>
+			ContainsPngData = 0x0E
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the box data.
+		/// </summary>
+		private ByteVector data;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleDataBox" /> with a provided header and handler
+		///    by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public AppleDataBox (BoxHeader header, TagLib.File file,
+		                     IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			Data = LoadData (file);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleDataBox" /> with specified data and flags.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the data to
+		///    store in the new instance.
+		/// </param>
+		/// <param name="flags">
+		///    A <see cref="uint" /> value containing flags to use for
+		///    the new instance.
+		/// </param>
+		public AppleDataBox (ByteVector data, uint flags)
+			: base ("data", 0, flags)
+		{
+			Data = data;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the position of the data contained in the current
+		///    instance, after any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position of
+		///    the data contained in the current instance.
+		/// </value>
+		protected override long DataPosition {
+			get {return base.DataPosition + 4;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    contained in the current instance.
+		/// </value>
+		public override ByteVector Data {
+			get {return data;}
+			set {data = value != null ? value : new ByteVector ();}
+		}
+		
+		/// <summary>
+		///    Gets and sets the text contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the text
+		///    contained in the current instance, or <see
+		///    langword="null" /> if the box is not flagged as
+		///    containing text.
+		/// </value>
+		public string Text {
+			get {
+				return ((Flags & (int)
+					FlagType.ContainsText) != 0) ?
+					Data.ToString (StringType.UTF8) : null;
+			}
+			set {
+				Flags = (int) FlagType.ContainsText;
+				Data = ByteVector.FromString (value,
+					StringType.UTF8);
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Renders the current instance, including its children, to
+		///    a new <see cref="ByteVector" /> object, preceeding the
+		///    contents with a specified block of data.
+		/// </summary>
+		/// <param name="topData">
+		///    A <see cref="ByteVector" /> object containing box
+		///    specific header data to preceed the content.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		protected override ByteVector Render (ByteVector topData)
+		{
+			ByteVector output = new ByteVector (4);
+			output.Add (topData);
+			return base.Render (output);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/AppleElementaryStreamDescriptor.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleElementaryStreamDescriptor.cs
new file mode 100644
index 0000000..e0ebb88
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleElementaryStreamDescriptor.cs
@@ -0,0 +1,314 @@
+//
+// AppleElementaryStreamDescriptor.cs: Provides an implementation of an Apple
+// ItemListBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of an Apple ElementaryStreamDescriptor.
+	/// </summary>
+	/// <remarks>
+	///    This box may appear as a child of a <see
+	///    cref="IsoAudioSampleEntry" /> and provided further information
+	///    about an audio stream.
+	/// </remarks>
+	public class AppleElementaryStreamDescriptor : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the stream ID.
+		/// </summary>
+		private ushort es_id;
+		
+		/// <summary>
+		///    Contains the stream priority.
+		/// </summary>
+		private byte stream_priority;
+		
+		/// <summary>
+		///    Contains the object type ID.
+		/// </summary>
+		private byte object_type_id;
+		
+		/// <summary>
+		///    Contains the stream type.
+		/// </summary>
+		private byte stream_type;
+		
+		/// <summary>
+		///    Contains the bugger size.
+		/// </summary>
+		private uint buffer_size_db;
+		
+		/// <summary>
+		///    Contains the maximum bitrate.
+		/// </summary>
+		private uint max_bitrate;
+		
+		/// <summary>
+		///    Contains the average bitrate.
+		/// </summary>
+		private uint average_bitrate;
+		
+		/// <summary>
+		///    Contains the decoder config.
+		/// </summary>
+		private ByteVector decoder_config;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleElementaryStreamDescriptor" /> with a provided
+		///    header and handler by reading the contents from a
+		///    specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    Valid data could not be read.
+		/// </exception>
+		public AppleElementaryStreamDescriptor (BoxHeader header,
+		                                        TagLib.File file,
+		                                        IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			int offset = 0;
+			ByteVector box_data = file.ReadBlock (DataSize);
+			decoder_config = new ByteVector ();
+			
+			// Elementary Stream Descriptor Tag
+			if (box_data [offset ++] == 3) {
+				// We have a descriptor tag. Check that it's at
+				// least 20 long.
+				if (ReadLength (box_data, ref offset) < 20)
+					throw new CorruptFileException (
+						"Insufficient data present.");
+				
+				es_id = box_data.Mid (offset, 2).ToUShort ();
+				offset += 2;
+				stream_priority = box_data [offset ++];
+			} else {
+				// The tag wasn't found, so the next two byte
+				// are the ID, and after that, business as
+				// usual.
+				es_id = box_data.Mid (offset, 2).ToUShort ();
+				offset += 2;
+			}
+			
+			// Verify that the next data is the Decoder
+			// Configuration Descriptor Tag and escape if it won't
+			// work out.
+			if (box_data [offset ++] != 4)
+				throw new CorruptFileException (
+					"Could not identify decoder configuration descriptor.");
+			
+			// Check that it's at least 15 long.
+			if (ReadLength (box_data, ref offset) < 15)
+				throw new CorruptFileException (
+					"Could not read data. Too small.");
+			
+			// Read a lot of good info.
+			object_type_id = box_data [offset ++];
+			stream_type = box_data [offset ++];
+			buffer_size_db = box_data.Mid (offset, 3).ToUInt ();
+			offset += 3;
+			max_bitrate = box_data.Mid (offset, 4).ToUInt ();
+			offset += 4;
+			average_bitrate = box_data.Mid (offset, 4).ToUInt ();
+			offset += 4;
+			
+			// Verify that the next data is the Decoder Specific
+			// Descriptor Tag and escape if it won't work out.
+			if (box_data [offset ++] != 5)
+				throw new CorruptFileException (
+					"Could not identify decoder specific descriptor.");
+			
+			// The rest of the info is decoder specific.
+			uint length = ReadLength (box_data, ref offset);
+			decoder_config = box_data.Mid (offset, (int) length);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the ID of the stream described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value containing the ID of the
+		///    stream described by the current instance.
+		/// </value>
+		public ushort StreamId {
+			get {return es_id;}
+		}
+		
+		/// <summary>
+		///    Gets the priority of the stream described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value containing the priority of
+		///    the stream described by the current instance.
+		/// </value>
+		public byte StreamPriority {
+			get {return stream_priority;}
+		}
+		
+		/// <summary>
+		///    Gets the object type ID of the stream described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value containing the object type ID
+		///    of the stream described by the current instance.
+		/// </value>
+		public byte ObjectTypeId {
+			get {return object_type_id;}
+		}
+		
+		/// <summary>
+		///    Gets the type the stream described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value containing the type the
+		///    stream described by the current instance.
+		/// </value>
+		public byte StreamType {
+			get {return stream_type;}
+		}
+		
+		/// <summary>
+		///    Gets the buffer size DB value the stream described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the buffer size DB
+		///    value the stream described by the current instance.
+		/// </value>
+		public uint BufferSizeDB {
+			get {return buffer_size_db;}
+		}
+		
+		/// <summary>
+		///    Gets the maximum bitrate the stream described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the maximum
+		///    bitrate the stream described by the current instance.
+		/// </value>
+		public uint MaximumBitrate {
+			get {return max_bitrate / 1000;}
+		}
+		
+		/// <summary>
+		///    Gets the maximum average the stream described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the average
+		///    bitrate the stream described by the current instance.
+		/// </value>
+		public uint AverageBitrate {
+			get {return average_bitrate / 1000;}
+		}
+		
+		/// <summary>
+		///    Gets the decoder config data of stream described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the decoder
+		///    config data of the stream described by the current
+		///    instance.
+		/// </value>
+		public ByteVector DecoderConfig {
+			get {return decoder_config;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Reads a section length and updates the offset to the end
+		///    of of the length block.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object to read from.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value reference specifying the
+		///    offset at which to read. This value gets updated to the
+		///    position following the size data.
+		/// </param>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the length that
+		///    was read.
+		/// </returns>
+		private static uint ReadLength (ByteVector data, ref int offset)
+		{
+			byte b;
+			int end = offset + 4;
+			uint length = 0;
+			
+			do {
+				b = data [offset ++];
+				length = (uint) (length << 7) |
+					(uint) (b & 0x7f);
+			} while ((b & 0x80) != 0 && offset <= end);
+			
+			return length;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/AppleItemListBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleItemListBox.cs
new file mode 100644
index 0000000..cc0505f
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/AppleItemListBox.cs
@@ -0,0 +1,105 @@
+//
+// AppleItemListBox.cs: Provides an implementation of an Apple ItemListBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide an
+	///    implementation of an Apple ItemListBox.
+	/// </summary>
+	public class AppleItemListBox : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private IEnumerable<Box> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleItemListBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public AppleItemListBox (BoxHeader header, TagLib.File file,
+		                         IsoHandlerBox handler)
+			: base (header, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			children = LoadChildren (file);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AppleItemListBox" /> with no children.
+		/// </summary>
+		public AppleItemListBox () : base ("ilst")
+		{
+			children = new List<Box> ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override IEnumerable<Box> Children {
+			get {return children;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/FullBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/FullBox.cs
new file mode 100644
index 0000000..d6c683b
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/FullBox.cs
@@ -0,0 +1,210 @@
+//
+// FullBox.cs: Provides an implementation of a ISO/IEC 14496-12 FullBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 FullBox.
+	/// </summary>
+	public abstract class FullBox : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the box version.
+		/// </summary>
+		private byte version;
+		
+		/// <summary>
+		///    Contains the box flags.
+		/// </summary>
+		private uint flags;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="FullBox" /> with a provided header and handler by
+		///    reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		protected FullBox (BoxHeader header, TagLib.File file,
+		                   IsoHandlerBox handler)
+			: base (header, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Seek (base.DataPosition);
+			ByteVector header_data = file.ReadBlock (4);
+			version = header_data [0];
+			flags = header_data.Mid (1, 3).ToUInt ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="FullBox" /> with a provided header, version, and
+		///    flags.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the version of the
+		///    new instance.
+		/// </param>
+		/// <param name="flags">
+		///    A <see cref="byte" /> value containing the flags for the
+		///    new instance.
+		/// </param>
+		protected FullBox (BoxHeader header, byte version, uint flags)
+			: base (header)
+		{
+			this.version = version;
+			this.flags = flags;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="FullBox" /> with a provided header, version, and
+		///    flags.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="ByteVector" /> object containing the four
+		///    byte box type.
+		/// </param>
+		/// <param name="version">
+		///    A <see cref="byte" /> value containing the version of the
+		///    new instance.
+		/// </param>
+		/// <param name="flags">
+		///    A <see cref="byte" /> value containing the flags for the
+		///    new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="type" /> is <see langword="null" /> of
+		///    equal to "<c>uuid</c>".
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="type" /> isn't exactly 4 bytes long.
+		/// </exception>
+		protected FullBox (ByteVector type, byte version, uint flags)
+			: this (new BoxHeader (type), version, flags)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the position of the data contained in the current
+		///    instance, after any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position of
+		///    the data contained in the current instance.
+		/// </value>
+		protected override long DataPosition {
+			get {return base.DataPosition + 4;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the version number of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="byte" /> value containing the version
+		///    number of the current instance.
+		/// </value>
+		public uint Version {
+			get {return version;}
+			set {version = (byte) value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the flags that apply to the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the flags that
+		///    apply to the current instance.
+		/// </value>
+		public uint Flags {
+			get {return flags;}
+			set {flags = value;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Renders the current instance, including its children, to
+		///    a new <see cref="ByteVector" /> object, preceeding the
+		///    contents with a specified block of data.
+		/// </summary>
+		/// <param name="topData">
+		///    A <see cref="ByteVector" /> object containing box
+		///    specific header data to preceed the content.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		protected override ByteVector Render (ByteVector topData)
+		{
+			ByteVector output = new ByteVector ((byte) version);
+			output.Add (ByteVector.FromUInt (flags).Mid (1,3));
+			output.Add (topData);
+			
+			return base.Render (output);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoAudioSampleEntry.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoAudioSampleEntry.cs
new file mode 100644
index 0000000..273fa10
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoAudioSampleEntry.cs
@@ -0,0 +1,237 @@
+//
+// IsoAudioSampleEntry.cs: Provides an implementation of a ISO/IEC 14496-12
+// AudioSampleEntry and support for reading MPEG-4 video properties.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="IsoSampleEntry" /> and implements
+	///    <see cref="IAudioCodec" /> to provide an implementation of a
+	///    ISO/IEC 14496-12 AudioSampleEntry and support for reading MPEG-4
+	///    video properties.
+	/// </summary>
+	public class IsoAudioSampleEntry : IsoSampleEntry, IAudioCodec
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the channel count.
+		/// </summary>
+		private ushort channel_count;
+
+		/// <summary>
+		///    Contains the sample size.
+		/// </summary>
+		private ushort sample_size;
+
+		/// <summary>
+		///    Contains the sample rate.
+		/// </summary>
+		private uint   sample_rate;
+		
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private IEnumerable<Box> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoVisualSampleEntry" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoAudioSampleEntry (BoxHeader header, TagLib.File file,
+		                            IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Seek (base.DataPosition + 8);
+			channel_count = file.ReadBlock (2).ToUShort ();
+			sample_size = file.ReadBlock (2).ToUShort ();
+			file.Seek (base.DataPosition + 16);
+			sample_rate = file.ReadBlock (4).ToUInt ();
+			children = LoadChildren (file);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the position of the data contained in the current
+		///    instance, after any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position of
+		///    the data contained in the current instance.
+		/// </value>
+		protected override long DataPosition {
+			get {return base.DataPosition + 20;}
+		}
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override IEnumerable<Box> Children {
+			get {return children;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region IAudioCodec Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TimeSpan.Zero" />.
+		/// </value>
+		public TimeSpan Duration {
+			get {return TimeSpan.Zero;}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Video" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {
+				return string.Format (
+					CultureInfo.InvariantCulture,
+					"MPEG-4 Audio ({0})", BoxType);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				AppleElementaryStreamDescriptor esds =
+					GetChildRecursively ("esds") as
+						AppleElementaryStreamDescriptor;
+				
+				// If we don't have an stream descriptor, we
+				// don't know what's what.
+				if (esds == null)
+					return 0;
+				
+				// Return from the elementary stream descriptor.
+				return (int) esds.AverageBitrate;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {return (int)(sample_rate >> 16);}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return channel_count;}
+		}
+		
+		/// <summary>
+		///    Gets the sample size of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample size of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleSize {
+			get {return sample_size;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoChunkLargeOffsetBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoChunkLargeOffsetBox.cs
new file mode 100644
index 0000000..f85b751
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoChunkLargeOffsetBox.cs
@@ -0,0 +1,190 @@
+//
+// IsoChunkLargeOffsetBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// ChunkLargeOffsetBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 ChunkLargeOffsetBox.
+	/// </summary>
+	/// <remarks>
+	///    <see cref="IsoChunkOffsetBox" /> and <see
+	///    cref="IsoChunkLargeOffsetBox" /> contain offsets of media data
+	///    within the file. As such, if the file changes by even one byte,
+	///    these values are devalidatated and the box will have to be
+	///    overwritten to maintain playability.
+	/// </remarks>
+	public class IsoChunkLargeOffsetBox : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the chunk offsets.
+		/// </summary>
+		private ulong [] offsets;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoChunkLargeOffsetBox" /> with a provided header
+		///    and handler by reading the contents from a specified
+		///    file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		public IsoChunkLargeOffsetBox (BoxHeader header,
+		                               TagLib.File file,
+		                               IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			ByteVector box_data = file.ReadBlock (DataSize);
+			
+			offsets = new ulong [(int)
+				box_data.Mid (0, 4).ToUInt ()];
+			
+			for (int i = 0; i < offsets.Length; i ++)
+				offsets [i] = box_data.Mid (4 + i * 8,
+					8).ToULong ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    contained in the current instance.
+		/// </value>
+		public override ByteVector Data {
+			get {
+				ByteVector output = ByteVector.FromUInt ((uint)
+					offsets.Length);
+				for (int i = 0; i < offsets.Length; i ++)
+					output.Add (ByteVector.FromULong (
+						offsets [i]));
+				
+				return output;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the offset table contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ulong[]" /> containing the offset table
+		///    contained in the current instance.
+		/// </value>
+		public ulong [] Offsets {
+			get {return offsets;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Overwrites the existing box in the file after updating
+		///    the table for a size change.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file to which
+		///    the current instance belongs and wo which modifications
+		///    must be applied.
+		/// </param>
+		/// <param name="sizeDifference">
+		///    A <see cref="long" /> value containing the size
+		///    change that occurred in the file.
+		/// </param>
+		/// <param name="after">
+		///    A <see cref="long" /> value containing the position in
+		///    the file after which offsets will be invalidated. If an
+		///    offset is before this point, it won't be updated.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <param name="file" /> is <see langword="null" />.
+		/// </exception>
+		public void Overwrite (File file, long sizeDifference,
+		                       long after)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Insert (Render (sizeDifference, after),
+				Header.Position, Size);
+		}
+      
+		/// <summary>
+		///    Renders the current instance after updating the table for
+		///    a size change.
+		/// </summary>
+		/// <param name="sizeDifference">
+		///    A <see cref="long" /> value containing the size
+		///    change that occurred in the file.
+		/// </param>
+		/// <param name="after">
+		///    A <see cref="long" /> value containing the position in
+		///    the file after which offsets will be invalidated. If an
+		///    offset is before this point, it won't be updated.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the file.
+		/// </returns>
+		public ByteVector Render (long sizeDifference, long after)
+		{
+			for (int i = 0; i < offsets.Length; i ++)
+				if (offsets [i] >= (ulong) after)
+					offsets [i] = (ulong)
+						((long) offsets [i] +
+							sizeDifference);
+			
+			return Render ();
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoChunkOffsetBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoChunkOffsetBox.cs
new file mode 100644
index 0000000..464206f
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoChunkOffsetBox.cs
@@ -0,0 +1,187 @@
+//
+// IsoChunkOffsetBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// ChunkOffsetBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 ChunkOffsetBox.
+	/// </summary>
+	/// <remarks>
+	///    <see cref="IsoChunkOffsetBox" /> and <see
+	///    cref="IsoChunkLargeOffsetBox" /> contain offsets of media data
+	///    within the file. As such, if the file changes by even one byte,
+	///    these values are devalidatated and the box will have to be
+	///    overwritten to maintain playability.
+	/// </remarks>
+	public class IsoChunkOffsetBox : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the chunk offsets.
+		/// </summary>
+		private uint [] offsets;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoChunkOffsetBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		public IsoChunkOffsetBox (BoxHeader header, TagLib.File file,
+		                          IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			ByteVector box_data = file.ReadBlock (DataSize);
+			
+			offsets = new uint [(int)
+				box_data.Mid (0, 4).ToUInt ()];
+			
+			for (int i = 0; i < offsets.Length; i ++)
+				offsets [i] = box_data.Mid (4 + i * 4,
+					4).ToUInt ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    contained in the current instance.
+		/// </value>
+		public override ByteVector Data {
+			get {
+				ByteVector output = ByteVector.FromUInt ((uint)
+					offsets.Length);
+				for (int i = 0; i < offsets.Length; i ++)
+					output.Add (ByteVector.FromUInt (
+						offsets [i]));
+				
+				return output;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the offset table contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint[]" /> containing the offset table
+		///    contained in the current instance.
+		/// </value>
+		public uint [] Offsets {
+			get {return offsets;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Overwrites the existing box in the file after updating
+		///    the table for a size change.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file to which
+		///    the current instance belongs and wo which modifications
+		///    must be applied.
+		/// </param>
+		/// <param name="sizeDifference">
+		///    A <see cref="long" /> value containing the size
+		///    change that occurred in the file.
+		/// </param>
+		/// <param name="after">
+		///    A <see cref="long" /> value containing the position in
+		///    the file after which offsets will be invalidated. If an
+		///    offset is before this point, it won't be updated.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <param name="file" /> is <see langword="null" />.
+		/// </exception>
+		public void Overwrite (File file, long sizeDifference,
+		                       long after)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Insert (Render (sizeDifference, after),
+				Header.Position, Size);
+		}
+		
+		/// <summary>
+		///    Renders the current instance after updating the table for
+		///    a size change.
+		/// </summary>
+		/// <param name="sizeDifference">
+		///    A <see cref="long" /> value containing the size
+		///    change that occurred in the file.
+		/// </param>
+		/// <param name="after">
+		///    A <see cref="long" /> value containing the position in
+		///    the file after which offsets will be invalidated. If an
+		///    offset is before this point, it won't be updated.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the file.
+		/// </returns>
+		public ByteVector Render (long sizeDifference, long after)
+		{
+			for (int i = 0; i < offsets.Length; i ++)
+				if (offsets [i] >= (uint) after)
+					offsets [i] = (uint)
+						(offsets [i] + sizeDifference);
+			
+			return Render ();
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoFreeSpaceBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoFreeSpaceBox.cs
new file mode 100644
index 0000000..c1da4c5
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoFreeSpaceBox.cs
@@ -0,0 +1,117 @@
+//
+// IsoFreeSpaceBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// FreeSpaceBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 FreeSpaceBox.
+	/// </summary>
+	public class IsoFreeSpaceBox : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the size of the padding.
+		/// </summary>
+		private long padding;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoMetaBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		public IsoFreeSpaceBox (BoxHeader header, TagLib.File file,
+		                        IsoHandlerBox handler)
+			: base (header, handler)
+		{
+			padding = DataSize;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoFreeSpaceBox" /> to occupy a specified number of
+		///    bytes.
+		/// </summary>
+		/// <param name="padding">
+		///    A <see cref="long" /> value specifying the number of
+		///    bytes the new instance should occupy when rendered.
+		/// </param>
+		public IsoFreeSpaceBox (long padding) : base ("free")
+		{
+			PaddingSize = padding;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the data
+		///    contained in the current instance.
+		/// </value>
+		public override ByteVector Data {
+			get {return new ByteVector ((int) padding);}
+			set {padding = (value != null) ? value.Count : 0;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the size the current instance will occupy
+		///    when rendered.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the size the
+		///    current instance will occupy when rendered.
+		/// </value>
+		public long PaddingSize {
+			get {return padding + 8;}
+			set {padding = value - 8;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoHandlerBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoHandlerBox.cs
new file mode 100644
index 0000000..1a30f45
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoHandlerBox.cs
@@ -0,0 +1,174 @@
+//
+// IsoHandlerBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// HandlerBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 FullBox.
+	/// </summary>
+	public class IsoHandlerBox : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the handler type.
+		/// </summary>
+		private ByteVector handler_type;
+		
+		/// <summary>
+		///    Contains the handler name.
+		/// </summary>
+		private string name;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoHandlerBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoHandlerBox (BoxHeader header, TagLib.File file,
+		                      IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			if (file == null)
+				throw new System.ArgumentNullException ("file");
+			
+			file.Seek (DataPosition + 4);
+			ByteVector box_data = file.ReadBlock (DataSize - 4);
+			handler_type = box_data.Mid (0, 4);
+			
+			int end = box_data.Find ((byte) 0, 16);
+			if (end < 16)
+				end = box_data.Count;
+			name = box_data.ToString (StringType.UTF8, 16, end - 16);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoHandlerBox" /> with a specified type and name.
+		/// </summary>
+		/// <param name="handlerType">
+		///    A <see cref="ByteVector" /> object specifying a 4 byte
+		///    handler type.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="string" /> object specifying the handler
+		///    name.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="handlerType" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="handlerType" /> is less than 4 bytes
+		///    long.
+		/// </exception>
+		public IsoHandlerBox (ByteVector handlerType, string name)
+			: base ("hdlr", 0, 0)
+		{
+			if (handlerType == null)
+				throw new ArgumentNullException ("handlerType");
+			
+			if (handlerType.Count < 4)
+				throw new ArgumentException (
+					"The handler type must be four bytes long.",
+					"handlerType");
+			
+			this.handler_type = handlerType.Mid (0,4);
+			this.name = name;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the data contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the data contained in the current
+		///    instance.
+		/// </value>
+		public override ByteVector Data {
+			get {
+				ByteVector output = new ByteVector (4);
+				output.Add (handler_type);
+				output.Add (new ByteVector (12));
+				output.Add (ByteVector.FromString (name,
+					StringType.UTF8));
+				output.Add (new ByteVector (2));
+				return output;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the handler type of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the
+		///    handler type of the current instance.
+		/// </value>
+		public ByteVector HandlerType {
+			get {return handler_type;}
+		}
+		
+		/// <summary>
+		///    Gets the name of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the name of the
+		///    current instance.
+		/// </value>
+		public string Name {
+			get {return name;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoMetaBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoMetaBox.cs
new file mode 100644
index 0000000..9612732
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoMetaBox.cs
@@ -0,0 +1,128 @@
+//
+// IsoMetaBox.cs: Provides an implementation of a ISO/IEC 14496-12 MetaBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 MetaBox.
+	/// </summary>
+	public class IsoMetaBox : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private IEnumerable<Box> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoMetaBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoMetaBox (BoxHeader header, TagLib.File file,
+		                   IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			children = LoadChildren (file);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoMetaBox" /> with a specified handler.
+		/// </summary>
+		/// <param name="handlerType">
+		///    A <see cref="ByteVector" /> object specifying a 4 byte
+		///    handler type.
+		/// </param>
+		/// <param name="handlerName">
+		///    A <see cref="string" /> object specifying the handler
+		///    name.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="handlerType" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="handlerType" /> is less than 4 bytes
+		///    long.
+		/// </exception>
+		public IsoMetaBox (ByteVector handlerType, string handlerName)
+			: base ("meta", 0, 0)
+		{
+			if (handlerType == null)
+				throw new ArgumentNullException ("handlerType");
+			
+			if (handlerType.Count < 4)
+				throw new ArgumentException (
+					"The handler type must be four bytes long.",
+					"handlerType");
+			
+			children = new List<Box> ();
+			AddChild (new IsoHandlerBox (handlerType, handlerName));
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override IEnumerable<Box> Children {
+			get {return children;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoMovieHeaderBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoMovieHeaderBox.cs
new file mode 100644
index 0000000..f61387f
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoMovieHeaderBox.cs
@@ -0,0 +1,248 @@
+//
+// IsoMovieHeaderBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// MovieHeaderBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 MovieHeaderBox.
+	/// </summary>
+	public class IsoMovieHeaderBox : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the creation time of the movie.
+		/// </summary>
+		private ulong creation_time;
+		
+		/// <summary>
+		///    Contains the modification time of the movie.
+		/// </summary>
+		private ulong modification_time;
+		
+		/// <summary>
+		///    Contains the timescale.
+		/// </summary>
+		private uint timescale;
+		
+		/// <summary>
+		///    Contains the duration.
+		/// </summary>
+		private ulong  duration;
+		
+		/// <summary>
+		///    Contains the rate.
+		/// </summary>
+		private uint rate;
+		
+		/// <summary>
+		///    Contains the volume.
+		/// </summary>
+		private ushort volume;
+		
+		/// <summary>
+		///    Contains the next track ID.
+		/// </summary>
+		private uint next_track_id;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoMovieHeaderBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoMovieHeaderBox (BoxHeader header, TagLib.File file,
+		                          IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			int bytes_remaining = DataSize;
+			ByteVector data;
+			
+			if (Version == 1) {
+				// Read version one (large integers).
+				data = file.ReadBlock (Math.Min (28,
+					bytes_remaining));
+				if (data.Count >= 8)
+					creation_time = data.Mid (0,
+						8).ToULong ();
+				if (data.Count >= 16)
+					modification_time = data.Mid (8,
+						8).ToULong ();
+				if (data.Count >= 20)
+					timescale = data.Mid (16, 4).ToUInt ();
+				if (data.Count >= 28)
+					duration = data.Mid (20, 8).ToULong ();
+				bytes_remaining -= 28;
+			} else {
+				// Read version zero (normal integers).
+				data = file.ReadBlock (Math.Min (16,
+					bytes_remaining));
+				if (data.Count >= 4)
+					creation_time = data.Mid (0,
+						4).ToUInt ();
+				if (data.Count >= 8)
+					modification_time = data.Mid (4,
+						4).ToUInt ();
+				if (data.Count >= 12)
+					timescale = data.Mid (8, 4).ToUInt ();
+				if (data.Count >= 16)
+					duration = data.Mid (12, 4).ToUInt ();
+				bytes_remaining -= 16;
+			}
+			
+			data = file.ReadBlock (Math.Min (6, bytes_remaining));
+			if (data.Count >= 4)
+				rate = data.Mid (0, 4).ToUInt ();
+			if (data.Count >= 6)
+				volume = data.Mid (4, 2).ToUShort ();
+			file.Seek (file.Tell + 70);
+			bytes_remaining -= 76;
+			
+			data = file.ReadBlock (Math.Min (4,
+				bytes_remaining));
+			
+			if (data.Count >= 4)
+				next_track_id = data.Mid (0, 4).ToUInt ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the creation time of movie represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="DateTime" /> value containing the creation
+		///    time of the movie represented by the current instance.
+		/// </value>
+		public DateTime CreationTime {
+			get {
+				return new System.DateTime (1904, 1, 1, 0, 0,
+					0).AddTicks ((long)(10000000 *
+						creation_time));
+			}
+		}
+		
+		/// <summary>
+		///    Gets the modification time of movie represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="DateTime" /> value containing the
+		///    modification time of the movie represented by the current
+		///    instance.
+		/// </value>
+		public DateTime ModificationTime {
+			get {
+				return new System.DateTime (1904, 1, 1, 0, 0,
+					0).AddTicks ((long)(10000000 *
+						modification_time));
+			}
+		}
+		
+		/// <summary>
+		///    Gets the duration of the movie represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> value containing the duration
+		///    of the movie represented by the current instance.
+		/// </value>
+		public TimeSpan Duration {
+			get {
+				// The length is the number of ticks divided by
+				// ticks per second.
+				return TimeSpan.FromSeconds ((double) duration /
+					(double) timescale);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the playback rate of the movie represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="double" /> value containing the playback
+		///    rate of the movie represented by the current instance.
+		/// </value>
+		public double Rate {
+			get {return ((double) rate) / ((double) 0x10000);}
+		}
+		
+		/// <summary>
+		///    Gets the playback volume of the movie represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="double" /> value containing the playback
+		///    volume of the movie represented by the current instance.
+		/// </value>
+		public double Volume {
+			get {return ((double) volume) / ((double) 0x100);}
+		}
+		
+		/// <summary>
+		///    Gets the ID of the next track in the movie represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///   A <see cref="uint" /> value containing the ID of the next
+		///   track in the movie represented by the current instance.
+		/// </value>
+		public uint NextTrackId {
+			get {return next_track_id;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleDescriptionBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleDescriptionBox.cs
new file mode 100644
index 0000000..0a91c01
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleDescriptionBox.cs
@@ -0,0 +1,132 @@
+//
+// IsoSampleDescriptionBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// SampleDescriptionBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="FullBox" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 SampleDescriptionBox.
+	/// </summary>
+	public class IsoSampleDescriptionBox : FullBox
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the number of entries at the beginning of the
+		///    children that will be of type <see cref="IsoSampleEntry"
+		///    />, regardless of their box type.
+		/// </summary>
+		private uint entry_count;
+		
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private IEnumerable<Box> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoSampleDescriptionBox" /> with a provided header
+		///    and handler by reading the contents from a specified
+		///    file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoSampleDescriptionBox (BoxHeader header,
+		                                TagLib.File file,
+		                                IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			entry_count = file.ReadBlock (4).ToUInt ();
+			children = LoadChildren (file);
+		}
+		
+		#endregion
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the position of the data contained in the current
+		///    instance, after any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position of
+		///    the data contained in the current instance.
+		/// </value>
+		protected override long DataPosition {
+			get {return base.DataPosition + 4;}
+		}
+		
+		/// <summary>
+		///    Gets the number of boxes at the begining of the children
+		///    that will be stored as <see cref="IsoAudioSampleEntry" />
+		///    of <see cref="IsoVisualSampleEntry" /> objects, depending
+		///    on the handler.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    children that will appear as sample entries.
+		/// </value>
+		public uint EntryCount {
+			get {return entry_count;}
+		}
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override IEnumerable<Box> Children {
+			get {return children;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleEntry.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleEntry.cs
new file mode 100644
index 0000000..574481a
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleEntry.cs
@@ -0,0 +1,109 @@
+//
+// IsoSampleEntry.cs: Provides an implementation of a ISO/IEC 14496-12
+// SampleEntry.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 SampleEntry.
+	/// </summary>
+	public class IsoSampleEntry : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the data reference index.
+		/// </summary>
+		private ushort data_reference_index;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoSampleEntry" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoSampleEntry (BoxHeader header, TagLib.File file,
+		                       IsoHandlerBox handler)
+			: base (header, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Seek (base.DataPosition + 6);
+			data_reference_index = file.ReadBlock (2).ToUShort ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the position of the data contained in the current
+		///    instance, after any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position of
+		///    the data contained in the current instance.
+		/// </value>
+		protected override long DataPosition {
+			get {return base.DataPosition + 8;}
+		}
+		
+		/// <summary>
+		///    Gets the data reference index of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value containing the data
+		///    reference index of the current instance.
+		/// </value>
+		public ushort DataReferenceIndex {
+			get {return data_reference_index;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleTableBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleTableBox.cs
new file mode 100644
index 0000000..4d8a8e1
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoSampleTableBox.cs
@@ -0,0 +1,97 @@
+//
+// IsoSampleTableBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// SampleTableBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 SampleTableBox.
+	/// </summary>
+	public class IsoSampleTableBox : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private IEnumerable<Box> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoSampleTableBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoSampleTableBox (BoxHeader header, TagLib.File file,
+		                         IsoHandlerBox handler)
+			: base (header, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			children = LoadChildren (file);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override IEnumerable<Box> Children {
+			get {return children;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoUserDataBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoUserDataBox.cs
new file mode 100644
index 0000000..750c06e
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoUserDataBox.cs
@@ -0,0 +1,106 @@
+//
+// IsoUserDataBox.cs: Provides an implementation of a ISO/IEC 14496-12
+// UserDataBox.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide an
+	///    implementation of a ISO/IEC 14496-12 UserDataBox.
+	/// </summary>
+	public class IsoUserDataBox : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private IEnumerable<Box> children;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoUserDataBox" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoUserDataBox (BoxHeader header, TagLib.File file,
+		                       IsoHandlerBox handler)
+			: base (header, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			children = LoadChildren (file);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoUserDataBox" /> with no children.
+		/// </summary>
+		public IsoUserDataBox () : base ("udta")
+		{
+			children = new List<Box> ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override IEnumerable<Box> Children {
+			get {return children;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/IsoVisualSampleEntry.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoVisualSampleEntry.cs
new file mode 100644
index 0000000..0283dfb
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/IsoVisualSampleEntry.cs
@@ -0,0 +1,197 @@
+//
+// IsoVisualSampleEntry.cs: Provides an implementation of a ISO/IEC 14496-12
+// VisualSampleEntry and support for reading MPEG-4 video properties.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Globalization;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="IsoSampleEntry" /> and implements
+	///    <see cref="IVideoCodec" /> to provide an implementation of a
+	///    ISO/IEC 14496-12 VisualSampleEntry and support for reading MPEG-4
+	///    video properties.
+	/// </summary>
+	public class IsoVisualSampleEntry : IsoSampleEntry, IVideoCodec
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the width of the visual.
+		/// </summary>
+		private ushort width;
+		
+		/// <summary>
+		///    Contains the height of the visual.
+		/// </summary>
+		private ushort height;
+		
+		/*
+		/// <summary>
+		///    Contains the children of the box.
+		/// </summary>
+		private BoxList children;
+		*/
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="IsoVisualSampleEntry" /> with a provided header and
+		///    handler by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public IsoVisualSampleEntry (BoxHeader header, TagLib.File file,
+		                             IsoHandlerBox handler)
+			: base (header, file, handler)
+		{
+			file.Seek (base.DataPosition + 16);
+			width = file.ReadBlock (2).ToUShort ();
+			height = file.ReadBlock (2).ToUShort ();
+			
+			/*
+			TODO: What are the children anyway?
+			children = LoadChildren (file);
+			*/
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the position of the data contained in the current
+		///    instance, after any box specific headers.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the position of
+		///    the data contained in the current instance.
+		/// </value>
+		protected override long DataPosition {
+			get {return base.DataPosition + 62;}
+		}
+		
+		/*
+		/// <summary>
+		///    Gets the children of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IEnumerable{T}" /> object enumerating the
+		///    children of the current instance.
+		/// </value>
+		public override BoxList Children {
+			get {return children;}
+		}
+		*/
+		
+		#endregion
+		
+		
+		
+		#region IVideoCodec Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TimeSpan.Zero" />.
+		/// </value>
+		public TimeSpan Duration {
+			get {return TimeSpan.Zero;}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Video" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Video;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {
+				return string.Format (
+					CultureInfo.InvariantCulture,
+					"MPEG-4 Video ({0})", BoxType);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the width of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> containing the width of the video
+		///    represented by the current instance.
+		/// </value>
+		public int VideoWidth {
+			get {return width;}
+		}
+		
+		/// <summary>
+		///    Gets the height of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> containing the height of the video
+		///    represented by the current instance.
+		/// </value>
+		public int VideoHeight {
+			get {return height;}
+		}
+		
+		#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Mpeg4/Boxes/UnknownBox.cs b/lib/TagLib/TagLib/Mpeg4/Boxes/UnknownBox.cs
new file mode 100644
index 0000000..dde9749
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/Boxes/UnknownBox.cs
@@ -0,0 +1,97 @@
+//
+// UnknownBox.cs: Provides a simple implementation of a box of unknown type.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="Box" /> to provide a simple
+	///    implementation of a box of unknown type.
+	/// </summary>
+	public class UnknownBox : Box
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the box's data.
+		/// </summary>
+		private ByteVector data;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnknownBox" /> with a provided header and handler
+		///    by reading the contents from a specified file.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="BoxHeader" /> object containing the header
+		///    to use for the new instance.
+		/// </param>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to read the contents
+		///    of the box from.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object containing the
+		///    handler that applies to the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		public UnknownBox (BoxHeader header, TagLib.File file,
+		                   IsoHandlerBox handler)
+			: base (header, handler)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			this.data = LoadData (file);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the box data contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the box
+		///    data contained in the current instance.
+		/// </value>
+		public override ByteVector Data {
+			get {return data;}
+			set {data = value;}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/File.cs b/lib/TagLib/TagLib/Mpeg4/File.cs
new file mode 100644
index 0000000..2a8ef00
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/File.cs
@@ -0,0 +1,419 @@
+//
+// File.cs: Provides tagging and properties support for MPEG-4 files.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide tagging
+	///    and properties support for MPEG-4 files.
+	/// </summary>
+	[SupportedMimeType("taglib/m4a", "m4a")]
+	[SupportedMimeType("taglib/m4b", "m4b")]
+	[SupportedMimeType("taglib/m4v", "m4v")]
+	[SupportedMimeType("taglib/m4p", "m4p")]
+	[SupportedMimeType("taglib/mp4", "mp4")]
+	[SupportedMimeType("audio/mp4")]
+	[SupportedMimeType("audio/x-m4a")]
+	[SupportedMimeType("video/mp4")]
+	[SupportedMimeType("video/x-m4v")]
+	public class File : TagLib.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the Apple tag.
+		/// </summary>
+		private AppleTag    apple_tag;
+		
+		/// <summary>
+		///    Contains the combined tag.
+		/// </summary>
+		/// <remarks>
+		///    TODO: Add support for ID3v2 tags.
+		/// </remarks>
+		private CombinedTag tag;
+		
+		/// <summary>
+		///    Contains the media properties.
+		/// </summary>
+		private Properties  properties;
+		
+		/// <summary>
+		///    Contains the ISO user data box.
+		/// </summary>
+		private IsoUserDataBox udta_box;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path)
+		{
+			Read (propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : this (path, ReadStyle.Average)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		                ReadStyle propertiesStyle)
+		: base (abstraction)
+		{
+			Read (propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: this (abstraction, ReadStyle.Average)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override TagLib.Tag Tag {
+			get {return tag;}
+		}
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties {
+			get {return properties;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			if (udta_box == null)
+				udta_box = new IsoUserDataBox ();
+			
+			// Try to get into write mode.
+			Mode = File.AccessMode.Write;
+			try {
+				FileParser parser = new FileParser (this);
+				parser.ParseBoxHeaders ();
+				
+				InvariantStartPosition = parser.MdatStartPosition;
+				InvariantEndPosition = parser.MdatEndPosition;
+				
+				long size_change = 0;
+				long write_position = 0;
+				
+				ByteVector tag_data = udta_box.Render ();
+				
+				// If we don't have a "udta" box to overwrite...
+				if (parser.UdtaTree == null ||
+					parser.UdtaTree.Length == 0 ||
+					parser.UdtaTree [parser.UdtaTree.Length - 1
+					].BoxType != BoxType.Udta) {
+					
+					// Stick the box at the end of the moov box.
+					BoxHeader moov_header = parser.MoovTree [
+						parser.MoovTree.Length - 1];
+					size_change = tag_data.Count;
+					write_position = moov_header.Position +
+						moov_header.TotalBoxSize;
+					Insert (tag_data, write_position, 0);
+					
+					// Overwrite the parent box sizes.
+					for (int i = parser.MoovTree.Length - 1; i >= 0;
+						i --)
+						size_change = parser.MoovTree [i
+							].Overwrite (this, size_change);
+				} else {
+					// Overwrite the old box.
+					BoxHeader udta_header = parser.UdtaTree [
+						parser.UdtaTree.Length - 1];
+					size_change = tag_data.Count -
+						udta_header.TotalBoxSize;
+					write_position = udta_header.Position;
+					Insert (tag_data, write_position,
+						udta_header.TotalBoxSize);
+					
+					// Overwrite the parent box sizes.
+					for (int i = parser.UdtaTree.Length - 2; i >= 0;
+						i --)
+						size_change = parser.UdtaTree [i
+							].Overwrite (this, size_change);
+				}
+				
+				// If we've had a size change, we may need to adjust
+				// chunk offsets.
+				if (size_change != 0) {
+					// We may have moved the offset boxes, so we
+					// need to reread.
+					parser.ParseChunkOffsets ();
+					InvariantStartPosition = parser.MdatStartPosition;
+					InvariantEndPosition = parser.MdatEndPosition;
+					
+					foreach (Box box in parser.ChunkOffsetBoxes) {
+						IsoChunkLargeOffsetBox co64 = 
+							box as IsoChunkLargeOffsetBox;
+						
+						if (co64 != null) {
+							co64.Overwrite (this,
+								size_change,
+								write_position);
+							continue;
+						}
+						
+						IsoChunkOffsetBox stco = 
+							box as IsoChunkOffsetBox;
+						
+						if (stco != null) {
+							stco.Overwrite (this,
+								size_change,
+								write_position);
+							continue;
+						}
+					}
+				}
+				
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = File.AccessMode.Closed;
+			}
+		}
+
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    At the time of this writing, only <see cref="AppleTag" />
+		///    is supported. All other tag types will be ignored.
+		/// </remarks>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			if (type == TagTypes.Apple) {
+				if (apple_tag == null && create) {
+					apple_tag = new AppleTag (udta_box);
+					tag.SetTags (apple_tag);
+				}
+				
+				return apple_tag;
+			}
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags (TagTypes types)
+		{
+			if ((types & TagTypes.Apple) != TagTypes.Apple ||
+				apple_tag == null)
+				return;
+			
+			apple_tag.DetachIlst ();
+			apple_tag = null;
+			tag.SetTags ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Reads the file with a specified read style.
+		/// </summary>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		private void Read (ReadStyle propertiesStyle)
+		{
+			// TODO: Support Id3v2 boxes!!!
+			tag = new CombinedTag ();
+			Mode = AccessMode.Read;
+			try {
+				FileParser parser = new FileParser (this);
+				
+				if (propertiesStyle == ReadStyle.None)
+					parser.ParseTag ();
+				else
+					parser.ParseTagAndProperties ();
+				
+				InvariantStartPosition = parser.MdatStartPosition;
+				InvariantEndPosition = parser.MdatEndPosition;
+				
+				udta_box = parser.UserDataBox;
+				
+				if (udta_box != null && udta_box.GetChild (BoxType.Meta)
+					!= null && udta_box.GetChild (BoxType.Meta
+					).GetChild (BoxType.Ilst) != null)
+					TagTypesOnDisk |= TagTypes.Apple;
+				
+				if (udta_box == null)
+					udta_box = new IsoUserDataBox ();
+				
+				apple_tag = new AppleTag (udta_box);
+				tag.SetTags (apple_tag);
+				
+				// If we're not reading properties, we're done.
+				if (propertiesStyle == ReadStyle.None) {
+					Mode = AccessMode.Closed;
+					return;
+				}
+				
+				// Get the movie header box.
+				IsoMovieHeaderBox mvhd_box = parser.MovieHeaderBox;
+				if(mvhd_box == null) {
+					Mode = AccessMode.Closed;
+					throw new CorruptFileException (
+						"mvhd box not found.");
+				}
+				
+				IsoAudioSampleEntry  audio_sample_entry =
+					parser.AudioSampleEntry;
+				IsoVisualSampleEntry visual_sample_entry =
+					parser.VisualSampleEntry;
+				
+				// Read the properties.
+				properties = new Properties (mvhd_box.Duration,
+					audio_sample_entry, visual_sample_entry);
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Mpeg4/FileParser.cs b/lib/TagLib/TagLib/Mpeg4/FileParser.cs
new file mode 100644
index 0000000..0135f89
--- /dev/null
+++ b/lib/TagLib/TagLib/Mpeg4/FileParser.cs
@@ -0,0 +1,603 @@
+//
+// FileParser.cs: Provides methods for reading important information from an
+// MPEG-4 file.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Mpeg4 {
+	/// <summary>
+	///    This class provides methods for reading important information
+	///    from an MPEG-4 file.
+	/// </summary>
+	public class FileParser
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the file to read from.
+		/// </summary>
+		private TagLib.File file;
+		
+		/// <summary>
+		///    Contains the first header found in the file.
+		/// </summary>
+		private BoxHeader first_header;
+		
+		/// <summary>
+		///    Contains the ISO movie header box.
+		/// </summary>
+		private IsoMovieHeaderBox mvhd_box;
+		
+		/// <summary>
+		///    Contains the ISO user data box.
+		/// </summary>
+		private IsoUserDataBox udta_box;
+		
+		/// <summary>
+		///    Contains the box headers from the top of the file to the
+		///    "moov" box.
+		/// </summary>
+		private BoxHeader [] moov_tree;
+		
+		/// <summary>
+		///    Contains the box headers from the top of the file to the
+		///    "udta" box.
+		/// </summary>
+		private BoxHeader [] udta_tree;
+		
+		/// <summary>
+		///    Contains the "stco" boxes found in the file.
+		/// </summary>
+		private List<Box> stco_boxes = new List<Box> ();
+		
+		/// <summary>
+		///    Contains the "stsd" boxes found in the file.
+		/// </summary>
+		private List<Box> stsd_boxes = new List<Box> ();
+		
+		/// <summary>
+		///    Contains the position at which the "mdat" box starts.
+		/// </summary>
+		private long mdat_start = -1;
+		
+		/// <summary>
+		///    Contains the position at which the "mdat" box ends.
+		/// </summary>
+		private long mdat_end = -1;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="FileParser" /> for a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object to perform operations
+		///    on.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="file" /> does not start with a
+		///    "<c>ftyp</c>" box.
+		/// </exception>
+		public FileParser (TagLib.File file)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			this.file = file;
+			first_header = new BoxHeader (file, 0);
+			
+			if (first_header.BoxType != "ftyp")
+				throw new CorruptFileException (
+					"File does not start with 'ftyp' box.");
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the movie header box read by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IsoMovieHeaderBox" /> object read by the
+		///    current instance, or <see langword="null" /> if not found.
+		/// </value>
+		/// <remarks>
+		///    This value will only be set by calling <see
+		///    cref="ParseTagAndProperties()" />.
+		/// </remarks>
+		public IsoMovieHeaderBox MovieHeaderBox {
+			get {return mvhd_box;}
+		}
+		
+		/// <summary>
+		///    Gets the user data box read by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IsoUserDataBox" /> object read by the
+		///    current instance, or <see langword="null" /> if not found.
+		/// </value>
+		/// <remarks>
+		///    This value will only be set by calling <see
+		///    cref="ParseTag()" /> and <see
+		///    cref="ParseTagAndProperties()" />.
+		/// </remarks>
+		public IsoUserDataBox UserDataBox {
+			get {return udta_box;}
+		}
+		
+		/// <summary>
+		///    Gets the audio sample entry read by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IsoAudioSampleEntry" /> object read by the
+		///    current instance, or <see langword="null" /> if not found.
+		/// </value>
+		/// <remarks>
+		///    This value will only be set by calling <see
+		///    cref="ParseTagAndProperties()" />.
+		/// </remarks>
+		public IsoAudioSampleEntry AudioSampleEntry {
+			get {
+				foreach (IsoSampleDescriptionBox box in stsd_boxes)
+					foreach (Box sub in box.Children) {
+						IsoAudioSampleEntry entry = sub
+							as IsoAudioSampleEntry;
+						
+						if (entry != null)
+							return entry;
+					}
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the visual sample entry read by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IsoVisualSampleEntry" /> object read by the
+		///    current instance, or <see langword="null" /> if not found.
+		/// </value>
+		/// <remarks>
+		///    This value will only be set by calling <see
+		///    cref="ParseTagAndProperties()" />.
+		/// </remarks>
+		public IsoVisualSampleEntry VisualSampleEntry {
+			get {
+				foreach (IsoSampleDescriptionBox box in stsd_boxes)
+					foreach (Box sub in box.Children) {
+						IsoVisualSampleEntry entry = sub
+							as IsoVisualSampleEntry;
+						
+						if (entry != null)
+							return entry;
+					}
+				return null;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the box headers for the first "<c>moov</c>" box and
+		///    all parent boxes up to the top of the file as read by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="BoxHeader[]" /> containing the headers for
+		///    the first "<c>moov</c>" box and its parent boxes up to
+		///    the top of the file, in the order they appear, or <see
+		///    langword="null" /> if none is present.
+		/// </value>
+		/// <remarks>
+		///    This value is useful for overwriting box headers, and is
+		///    only be set by calling <see cref="ParseBoxHeaders()" />.
+		/// </remarks>
+		public BoxHeader [] MoovTree {
+			get {return moov_tree;}
+		}
+		
+		/// <summary>
+		///    Gets the box headers for the first "<c>udta</c>" box and
+		///    all parent boxes up to the top of the file as read by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="BoxHeader[]" /> containing the headers for
+		///    the first "<c>udta</c>" box and its parent boxes up to
+		///    the top of the file, in the order they appear, or <see
+		///    langword="null" /> if none is present.
+		/// </value>
+		/// <remarks>
+		///    This value is useful for overwriting box headers, and is
+		///    only be set by calling <see cref="ParseBoxHeaders()" />.
+		/// </remarks>
+		public BoxHeader [] UdtaTree {
+			get {return udta_tree;}
+		}
+		
+		/// <summary>
+		///    Gets all chunk offset boxes read by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="Box[]" /> containing all chunk offset boxes
+		///    read by the current instance.
+		/// </value>
+		/// <remarks>
+		///    These boxes contain offset information for media data in
+		///    the current instance and can be devalidated by size
+		///    change operations, in which case they need to be
+		///    corrected. This value will only be set by calling <see
+		///    cref="ParseChunkOffsets()" />.
+		/// </remarks>
+		public Box [] ChunkOffsetBoxes {
+			get {return stco_boxes.ToArray ();}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the "<c>mdat</c>" box starts.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the "<c>mdat</c>" box starts.
+		/// </value>
+		/// <remarks>
+		///    The "<c>mdat</c>" box contains the media data for the
+		///    file and is used for estimating the invariant data
+		///    portion of the file.
+		/// </remarks>
+		public long MdatStartPosition {
+			get {return mdat_start;}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the "<c>mdat</c>" box ends.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the "<c>mdat</c>" box ends.
+		/// </value>
+		/// <remarks>
+		///    The "<c>mdat</c>" box contains the media data for the
+		///    file and is used for estimating the invariant data
+		///    portion of the file.
+		/// </remarks>
+		public long MdatEndPosition {
+			get {return mdat_end;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Parses the file referenced by the current instance,
+		///    searching for box headers that will be useful in saving
+		///    the file.
+		/// </summary>
+		public void ParseBoxHeaders ()
+		{
+			ResetFields ();
+			ParseBoxHeaders (first_header.TotalBoxSize,
+				file.Length, null);
+		}
+		
+		/// <summary>
+		///    Parses the file referenced by the current instance,
+		///    searching for tags.
+		/// </summary>
+		public void ParseTag ()
+		{
+			ResetFields ();
+			ParseTag (first_header.TotalBoxSize, file.Length);
+		}
+		
+		/// <summary>
+		///    Parses the file referenced by the current instance,
+		///    searching for tags and properties.
+		/// </summary>
+		public void ParseTagAndProperties ()
+		{
+			ResetFields ();
+			ParseTagAndProperties (first_header.TotalBoxSize,
+				file.Length, null);
+		}
+		
+		/// <summary>
+		///    Parses the file referenced by the current instance,
+		///    searching for chunk offset boxes.
+		/// </summary>
+		public void ParseChunkOffsets ()
+		{
+			ResetFields ();
+			ParseChunkOffsets (first_header.TotalBoxSize,
+				file.Length);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Parses boxes for a specified range, looking for headers.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to start reading.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to stop reading.
+		/// </param>
+		/// <param name="parents">
+		///    A <see cref="T:System.Collections.Generic.List`1" /> object containing all the parent
+		///    handlers that apply to the range.
+		/// </param>
+		private void ParseBoxHeaders (long start, long end,
+		                              List<BoxHeader> parents)
+		{
+			BoxHeader header;
+			
+			for (long position = start; position < end;
+				position += header.TotalBoxSize) {
+				header = new BoxHeader (file, position);
+				
+				if (moov_tree == null &&
+					header.BoxType == BoxType.Moov) {
+					List<BoxHeader> new_parents = AddParent (
+						parents, header);
+					moov_tree = new_parents.ToArray ();
+					ParseBoxHeaders (
+						header.HeaderSize + position,
+						header.TotalBoxSize + position,
+						new_parents);
+				} else if (header.BoxType == BoxType.Mdia ||
+					header.BoxType == BoxType.Minf ||
+					header.BoxType == BoxType.Stbl ||
+					header.BoxType == BoxType.Trak) {
+					ParseBoxHeaders (
+						header.HeaderSize + position,
+						header.TotalBoxSize + position,
+						AddParent (parents, header));
+				} else if (udta_tree == null &&
+					header.BoxType == BoxType.Udta) {
+					udta_tree = AddParent (parents,
+						header).ToArray ();
+				} else if (header.BoxType == BoxType.Mdat) {
+					mdat_start = position;
+					mdat_end = position + header.TotalBoxSize;
+				}
+				
+				if (header.TotalBoxSize == 0)
+					break;
+			}
+		}
+		
+		/// <summary>
+		///    Parses boxes for a specified range, looking for tags.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to start reading.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to stop reading.
+		/// </param>
+		private void ParseTag (long start, long end)
+		{
+			BoxHeader header;
+			
+			for (long position = start; position < end;
+				position += header.TotalBoxSize) {
+				header = new BoxHeader (file, position);
+				
+				if (header.BoxType == BoxType.Moov ||
+					header.BoxType == BoxType.Mdia ||
+					header.BoxType == BoxType.Minf ||
+					header.BoxType == BoxType.Stbl ||
+					header.BoxType == BoxType.Trak) {
+					ParseTag (header.HeaderSize + position,
+						header.TotalBoxSize + position);
+				} else if (udta_box == null &&
+					header.BoxType == BoxType.Udta) {
+					udta_box = BoxFactory.CreateBox (file,
+					header) as IsoUserDataBox;
+				} else if (header.BoxType == BoxType.Mdat) {
+					mdat_start = position;
+					mdat_end = position + header.TotalBoxSize;
+				}
+				
+				if (header.TotalBoxSize == 0)
+					break;
+			}
+		}
+		
+		/// <summary>
+		///    Parses boxes for a specified range, looking for tags and
+		///    properties.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to start reading.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to stop reading.
+		/// </param>
+		/// <param name="handler">
+		///    A <see cref="IsoHandlerBox" /> object that applied to the
+		///    range being searched.
+		/// </param>
+		private void ParseTagAndProperties (long start, long end,
+		                                    IsoHandlerBox handler)
+		{
+			BoxHeader header;
+			
+			for (long position = start; position < end;
+				position += header.TotalBoxSize) {
+				header = new BoxHeader (file, position);
+				ByteVector type = header.BoxType;
+				
+				if (type == BoxType.Moov ||
+					type == BoxType.Mdia ||
+					type == BoxType.Minf ||
+					type == BoxType.Stbl ||
+					type == BoxType.Trak) {
+					ParseTagAndProperties (
+						header.HeaderSize + position,
+						header.TotalBoxSize + position,
+						handler);
+				} else if (type == BoxType.Stsd) {
+					stsd_boxes.Add (BoxFactory.CreateBox (
+						file, header, handler));
+				} else if (type == BoxType.Hdlr) {
+					handler = BoxFactory.CreateBox (file,
+						header, handler) as
+							IsoHandlerBox;
+				} else if (mvhd_box == null &&
+					type == BoxType.Mvhd) {
+					mvhd_box = BoxFactory.CreateBox (file,
+						header, handler) as
+							IsoMovieHeaderBox;
+				} else if (udta_box == null &&
+					type == BoxType.Udta) {
+					udta_box = BoxFactory.CreateBox (file,
+						header, handler) as
+							IsoUserDataBox;
+				} else if (type == BoxType.Mdat) {
+					mdat_start = position;
+					mdat_end = position + header.TotalBoxSize;
+				}
+				
+				if (header.TotalBoxSize == 0)
+					break;
+			}
+		}
+		
+		/// <summary>
+		///    Parses boxes for a specified range, looking for chunk
+		///    offset boxes.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to start reading.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value specifying the seek position
+		///    at which to stop reading.
+		/// </param>
+		private void ParseChunkOffsets (long start, long end)
+		{
+			BoxHeader header;
+			
+			for (long position = start; position < end;
+				position += header.TotalBoxSize) {
+				header = new BoxHeader (file, position);
+				
+				if (header.BoxType == BoxType.Moov) {
+					ParseChunkOffsets (
+						header.HeaderSize + position,
+						header.TotalBoxSize + position);
+				} else if (header.BoxType == BoxType.Moov ||
+					header.BoxType == BoxType.Mdia ||
+					header.BoxType == BoxType.Minf ||
+					header.BoxType == BoxType.Stbl ||
+					header.BoxType == BoxType.Trak) {
+					ParseChunkOffsets (
+						header.HeaderSize + position,
+						header.TotalBoxSize + position);
+				} else if (header.BoxType == BoxType.Stco ||
+					header.BoxType == BoxType.Co64) {
+					stco_boxes.Add (BoxFactory.CreateBox (
+						file, header));
+				} else if (header.BoxType == BoxType.Mdat) {
+					mdat_start = position;
+					mdat_end = position + header.TotalBoxSize;
+				}
+				
+				if (header.TotalBoxSize == 0)
+					break;
+			}
+		}
+		
+		/// <summary>
+		///    Resets all internal fields.
+		/// </summary>
+		private void ResetFields ()
+		{
+			mvhd_box = null;
+			udta_box = null;
+			moov_tree = null;
+			udta_tree = null;
+			stco_boxes.Clear ();
+			stsd_boxes.Clear ();
+			mdat_start = -1;
+			mdat_end = -1;
+		}
+		
+		#endregion
+		
+		#region Private Static Methods
+		
+		/// <summary>
+		///    Adds a parent to the end of an existing list of parents.
+		/// </summary>
+		/// <param name="parents">
+		///    A <see cref="T:System.Collections.Generic.List`1" /> object containing an existing
+		///    list of parents.
+		/// </param>
+		/// <param name="current">
+		///    A <see cref="BoxHeader" /> object to add to the list.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="T:System.Collections.Generic.List`1" /> object containing the list
+		///    of parents, including the added header.
+		/// </returns>
+		private static List<BoxHeader> AddParent (List<BoxHeader> parents,
+		                                          BoxHeader current)
+		{
+			List<BoxHeader> boxes = new List<BoxHeader> ();
+			if (parents != null)
+				boxes.AddRange (parents);
+			boxes.Add (current);
+			return boxes;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/NonContainer/EndTag.cs b/lib/TagLib/TagLib/NonContainer/EndTag.cs
new file mode 100644
index 0000000..b79558a
--- /dev/null
+++ b/lib/TagLib/TagLib/NonContainer/EndTag.cs
@@ -0,0 +1,368 @@
+//
+// EndTag.cs: Provides support for accessing and modifying a collection of tags
+// appearing at the end of a file.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace TagLib.NonContainer {
+	/// <summary>
+	///    This class extends <see cref="CombinedTag" />, providing support
+	///    for accessing and modifying a collection of tags appearing at the
+	///    end of a file.
+	/// </summary>
+	/// <remarks>
+	///    <para>This class is used by <see cref="TagLib.NonContainer.File"
+	///    /> to read all the tags appearing at the end of the file but
+	///    could be used by other classes. It currently supports ID3v1,
+	///    ID3v2, and APE tags.</para>
+	/// </remarks>
+	public class EndTag : CombinedTag
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the file to operate on.
+		/// </summary>
+		private TagLib.File file;
+		
+		/// <summary>
+		///    Contains the number of bytes that must be read to
+		///    hold all applicable indicators.
+		/// </summary>
+		private static int read_size = (int) Math.Max (Math.Max (
+			TagLib.Ape.Footer.Size, TagLib.Id3v2.Footer.Size),
+			TagLib.Id3v1.Tag.Size);
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="EndTag" /> for a specified <see cref="TagLib.File"
+		///    />.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object on which the new
+		///    instance will perform its operations.
+		/// </param>
+		/// <remarks>
+		///    Constructing a new instance does not automatically read
+		///    the contents from the disk. <see cref="Read" /> must be
+		///    called to read the tags.
+		/// </remarks>
+		public EndTag (TagLib.File file) : base ()
+		{
+			this.file = file;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the total size of the tags located at the end of the
+		///    file by reading from the file.
+		/// </summary>
+		public long TotalSize {
+			get {
+				long start = file.Length;
+				
+				while (ReadTagInfo (ref start) != TagTypes.None)
+					;
+				
+				return file.Length - start;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Reads the tags stored at the end of the file into the
+		///    current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="long" /> value indicating the seek position
+		///    in the file at which the read tags begin. This also
+		///    marks the seek position at which the media ends.
+		/// </returns>
+		public long Read ()
+		{
+			TagLib.Tag tag;
+			ClearTags ();
+			long start = file.Length;
+			
+			while ((tag = ReadTag (ref start)) != null)
+				InsertTag (0, tag);
+			
+			return start;
+		}
+		
+		/// <summary>
+		///    Renders the tags contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    physical representation of the tags stored in the current
+		///    instance.
+		/// </returns>
+		/// <remarks>
+		///    The tags are rendered in the order that they are stored
+		///    in the current instance.
+		/// </remarks>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			foreach (TagLib.Tag t in Tags) {
+				if (t is TagLib.Ape.Tag)
+					data.Add ((t as TagLib.Ape.Tag).Render ());
+				else if (t is TagLib.Id3v2.Tag)
+					data.Add ((t as TagLib.Id3v2.Tag).Render ());
+				else if (t is TagLib.Id3v1.Tag)
+					data.Add ((t as TagLib.Id3v1.Tag).Render ());
+			}
+			
+			return data;
+		}
+		
+		/// <summary>
+		///    Writes the tags contained in the current instance to the
+		///    end of the file that created it, overwriting the existing
+		///    tags.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="long" /> value indicating the seek position
+		///    in the file at which the written tags begin. This also
+		///    marks the seek position at which the media ends.
+		/// </returns>
+		public long Write ()
+		{
+			long total_size = TotalSize;
+			ByteVector data = Render ();
+			file.Insert (data, file.Length - total_size, total_size);
+			return file.Length - data.Count;
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public void RemoveTags (TagTypes types)
+		{
+			for (int i = Tags.Length - 1; i >= 0; i--) {
+				var tag = Tags[i];
+				if (types == TagTypes.AllTags || (tag.TagTypes & types) == tag.TagTypes) {
+					RemoveTag (tag);
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Adds a tag of a specified type to the current instance,
+		///    optionally copying values from an existing type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagTypes" /> value specifying the type of
+		///    tag to add to the current instance. At the time of this
+		///    writing, this is limited to <see cref="TagTypes.Ape" />,
+		///    <see cref="TagTypes.Id3v1" />, and <see
+		///    cref="TagTypes.Id3v2" />.
+		/// </param>
+		/// <param name="copy">
+		///    A <see cref="TagLib.Tag" /> to copy values from using
+		///    <see cref="TagLib.Tag.CopyTo" />, or <see
+		///    langword="null" /> if no tag is to be copied.
+		/// </param>
+		/// <returns>
+		///    The <see cref="TagLib.Tag" /> object added to the current
+		///    instance, or <see langword="null" /> if it couldn't be
+		///    created.
+		/// </returns>
+		/// <remarks>
+		///    ID3v2 tags are added at the end of the current instance,
+		///    while other tags are added to the beginning.
+		/// </remarks>
+		public TagLib.Tag AddTag (TagTypes type, TagLib.Tag copy)
+		{
+			TagLib.Tag tag = null;
+			
+			if (type == TagTypes.Id3v1) {
+				tag = new TagLib.Id3v1.Tag ();
+			} else if (type == TagTypes.Id3v2) {
+				Id3v2.Tag tag32 = new Id3v2.Tag ();
+				tag32.Version = 4;
+				tag32.Flags |= Id3v2.HeaderFlags.FooterPresent;
+				tag = tag32;
+			} else if (type == TagTypes.Ape) {
+				tag = new TagLib.Ape.Tag ();
+			}
+			
+			if (tag != null) {
+				if (copy != null)
+					copy.CopyTo (tag, true);
+				
+				if (type == TagTypes.Id3v1)
+					AddTag (tag);
+				else
+					InsertTag (0, tag);
+			}
+			
+			return tag;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Reads a tag ending at a specified position and moves the
+		///    cursor to its start position.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value reference specifying at what
+		///    position the potential tag ends at. If a tag is found,
+		///    this value will be updated to the position at which the
+		///    found tag starts.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Tag" /> object representing the tag
+		///    found at the specified position, or <see langword="null"
+		///    /> if no tag was found.
+		/// </returns>
+		private TagLib.Tag ReadTag (ref long end)
+		{
+			long start = end;
+			TagTypes type = ReadTagInfo (ref start);
+			TagLib.Tag tag = null;
+			
+			try {
+				switch (type)
+				{
+				case TagTypes.Ape:
+					tag = new TagLib.Ape.Tag (file, end - TagLib.Ape.Footer.Size);
+					break;
+				case TagTypes.Id3v2:
+					tag = new TagLib.Id3v2.Tag (file, start);
+					break;
+				case TagTypes.Id3v1:
+					tag = new TagLib.Id3v1.Tag (file, start);
+					break;
+				}
+				
+				end = start;
+			} catch (CorruptFileException) {
+			}
+			
+			return tag;
+		}
+		
+		/// <summary>
+		///    Looks for a tag ending at a specified position and moves
+		///    the cursor to its start position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying at what
+		///    position the potential tag ends. If a tag is found,
+		///    this value will be updated to the position at which the
+		///    found tag starts.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.TagTypes" /> value specifying the
+		///    type of tag found at the specified position, or <see
+		///    cref="TagTypes.None" /> if no tag was found.
+		/// </returns>
+		private TagTypes ReadTagInfo (ref long position)
+		{
+			if (position - read_size < 0)
+				return TagTypes.None;
+			
+			file.Seek (position - read_size);
+			ByteVector data = file.ReadBlock (read_size);
+			
+			try {
+				int offset = (int) (data.Count - TagLib.Ape.Footer.Size);
+				if (data.ContainsAt (TagLib.Ape.Footer.FileIdentifier,
+					offset)) {
+					TagLib.Ape.Footer footer =
+						new TagLib.Ape.Footer (
+							data.Mid (offset));
+					
+					// If the complete tag size is zero or
+					// the tag is a header, this indicates
+					// some sort of corruption.
+					if (footer.CompleteTagSize == 0 ||
+						(footer.Flags &
+						TagLib.Ape.FooterFlags.IsHeader) != 0)
+						return TagTypes.None;
+					
+					position -= footer.CompleteTagSize;
+					return TagTypes.Ape;
+				}
+				
+				offset = (int) (data.Count - TagLib.Id3v2.Footer.Size);
+				if (data.ContainsAt (TagLib.Id3v2.Footer.FileIdentifier,
+					offset)) {
+					TagLib.Id3v2.Footer footer =
+						new TagLib.Id3v2.Footer (
+							data.Mid (offset));
+					
+					position -= footer.CompleteTagSize;
+					return TagTypes.Id3v2;
+				}
+				
+				if (data.StartsWith (
+					TagLib.Id3v1.Tag.FileIdentifier)) {
+					position -= TagLib.Id3v1.Tag.Size;
+					return TagTypes.Id3v1;
+				}
+			} catch (CorruptFileException) {
+			}
+			
+			return TagTypes.None;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/NonContainer/File.cs b/lib/TagLib/TagLib/NonContainer/File.cs
new file mode 100644
index 0000000..64e9e38
--- /dev/null
+++ b/lib/TagLib/TagLib/NonContainer/File.cs
@@ -0,0 +1,401 @@
+//
+// File.cs: Provides tagging and properties for files that contain an
+// indeterminite  number of tags at their beginning or end.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace TagLib.NonContainer {
+	/// <summary>
+	///    This abstract class extends <see cref="TagLib.File" /> to provide
+	///    tagging and properties for files that contain an indeterminite
+	///    number of tags at their beginning or end.
+	/// </summary>
+	/// <remarks>
+	///    <para>When extending this class, <see cref="ReadStart" />, <see
+	///    cref="ReadEnd" />, and <see cref="ReadProperties" /> should be
+	///    overrided methods that read the format specific information from
+	///    the file.</para>
+	///    <para>The file is read upon construction in the following
+	///    manner:</para>
+	///    <list type="number">
+	///       <item><term>The file is opened for reading.</term></item>
+	///       <item><term>The tags at the start of the file are
+	///       read.</term></item>
+	///       <item><term><see cref="ReadStart" /> is called.</term></item>
+	///       <item><term>The tags at the end of the file are
+	///       read.</term></item>
+	///       <item><term><see cref="ReadEnd" /> is called.</term></item>
+	///       <item><term>If reading with a style other than <see
+	///       cref="ReadStyle.None" />, <see cref="ReadProperties" /> is
+	///       called.</term></item>
+	///       <item><term>The file is closed.</term></item>
+	///    </list>
+	/// </remarks>
+	public abstract class File : TagLib.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the tags.
+		/// </summary>
+		private TagLib.NonContainer.Tag tag;
+		
+		/// <summary>
+		///    Contains the media properties.
+		/// </summary>
+		private Properties properties;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		protected File (string path, ReadStyle propertiesStyle)
+			: base (path)
+		{
+			Read (propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		protected File (string path) : this (path, ReadStyle.Average)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		protected File (File.IFileAbstraction abstraction,
+		                ReadStyle propertiesStyle)
+		: base (abstraction)
+		{
+			Read (propertiesStyle);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		protected File (File.IFileAbstraction abstraction)
+			: this (abstraction, ReadStyle.Average)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override TagLib.Tag Tag {
+			get {return tag;}
+		}
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties {
+			get {return properties;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			long start, end;
+			Mode = AccessMode.Write;
+			try {
+				tag.Write (out start, out end);
+				InvariantStartPosition = start;
+				InvariantEndPosition = end;
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags (TagTypes types)
+		{
+			tag.RemoveTags (types);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Properties
+		
+		/// <summary>
+		///    Gets the collection of tags appearing at the start of the
+		///    file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.NonContainer.StartTag" /> storing the
+		///    tags for the start of the file.
+		/// </value>
+		protected StartTag StartTag {
+			get {return tag.StartTag;}
+		}
+		
+		/// <summary>
+		///    Gets the collection of tags appearing at the end of the
+		///    file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.NonContainer.EndTag" /> storing the
+		///    tags for the end of the file.
+		/// </value>
+		protected EndTag EndTag {
+			get {return tag.EndTag;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <remarks>
+		///    This method is called by the constructor immediately
+		///    after the tags at the start of the file have been read
+		///    and as such (so the internal seek mechanism is close to
+		///    the start). It should be used for reading any content
+		///    specific information, such as an audio header from the
+		///    start of the file.
+		/// </remarks>
+		protected virtual void ReadStart (long start,
+		                                  ReadStyle propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <remarks>
+		///    This method is called by the constructor immediately
+		///    after the tags at the end of the file have been read
+		///    and as such (so the internal seek mechanism is close to
+		///    the end). It should be used for reading any content
+		///    specific information, such as an audio header from the
+		///    end of the file.
+		/// </remarks>
+		protected virtual void ReadEnd (long end,
+		                                ReadStyle propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		/// <remarks>
+		///    This method is called ONLY IF the file is constructed
+		///    with a read style other than <see cref="ReadStyle.None"
+		///    />, and as such MUST NOT return <see langword="null" />.
+		///    It is guaranteed that <see cref="ReadStart" /> and <see
+		///    cref="ReadEnd" /> will have been called first and this
+		///    method should be strictly used to perform final
+		///    processing on already read data.
+		/// </remarks>
+		protected abstract Properties ReadProperties (long start,
+		                                              long end,
+		                                              ReadStyle propertiesStyle);
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Reads the file with a specified read style.
+		/// </summary>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		private void Read (ReadStyle propertiesStyle)
+		{
+			Mode = AccessMode.Read;
+			try {
+				tag = new Tag (this);
+				
+				// Read the tags and property data at the beginning of
+				// the file.
+				InvariantStartPosition = tag.ReadStart ();
+				TagTypesOnDisk |= StartTag.TagTypes;
+				ReadStart (InvariantStartPosition, propertiesStyle);
+				
+				// Read the tags and property data at the end of the
+				// file.
+				InvariantEndPosition =
+					(InvariantStartPosition == Length) ?
+					Length : tag.ReadEnd ();
+				TagTypesOnDisk |= EndTag.TagTypes;
+				ReadEnd (InvariantEndPosition, propertiesStyle);
+				
+				// Read the audio properties.
+				properties = (propertiesStyle != ReadStyle.None) ?
+					ReadProperties (InvariantStartPosition,
+						InvariantEndPosition, propertiesStyle) :
+					null;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/NonContainer/StartTag.cs b/lib/TagLib/TagLib/NonContainer/StartTag.cs
new file mode 100644
index 0000000..e2873ae
--- /dev/null
+++ b/lib/TagLib/TagLib/NonContainer/StartTag.cs
@@ -0,0 +1,330 @@
+//
+// StartTag.cs: Provides support for accessing and modifying a collection of
+// tags appearing at the start of a file.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace TagLib.NonContainer {
+	/// <summary>
+	///    This class extends <see cref="CombinedTag" />, providing support
+	///    for accessing and modifying a collection of tags appearing at the
+	///    start of a file.
+	/// </summary>
+	/// <remarks>
+	///    <para>This class is used by <see cref="TagLib.NonContainer.File"
+	///    /> to read all the tags appearing at the start of the file but
+	///    could be used by other classes. It currently supports ID3v2
+	///    and APE tags.</para>
+	/// </remarks>
+	public class StartTag : CombinedTag
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the file to operate on.
+		/// </summary>
+		private TagLib.File file;
+		
+		/// <summary>
+		///    Contains the number of bytes that must be read to
+		///    hold all applicable indicators.
+		/// </summary>
+		int read_size = (int) Math.Max (TagLib.Ape.Footer.Size,
+			TagLib.Id3v2.Header.Size);
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StartTag" /> for a specified <see
+		///    cref="TagLib.File" />.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object on which the new
+		///    instance will perform its operations.
+		/// </param>
+		/// <remarks>
+		///    Constructing a new instance does not automatically read
+		///    the contents from the disk. <see cref="Read" /> must be
+		///    called to read the tags.
+		/// </remarks>
+		public StartTag (TagLib.File file) : base ()
+		{
+			this.file = file;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the total size of the tags located at the end of the
+		///    file by reading from the file.
+		/// </summary>
+		public long TotalSize {
+			get {
+				long size = 0;
+				
+				while (ReadTagInfo (ref size) != TagTypes.None)
+					;
+				
+				return size;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Reads the tags stored at the start of the file into the
+		///    current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="long" /> value indicating the seek position
+		///    in the file at which the read tags end. This also
+		///    marks the seek position at which the media begins.
+		/// </returns>
+		public long Read ()
+		{
+			TagLib.Tag tag;
+			ClearTags ();
+			long end = 0;
+			
+			while ((tag = ReadTag (ref end)) != null)
+				AddTag (tag);
+			
+			return end;
+		}
+		
+		/// <summary>
+		///    Renders the tags contained in the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    physical representation of the tags stored in the current
+		///    instance.
+		/// </returns>
+		/// <remarks>
+		///    The tags are rendered in the order that they are stored
+		///    in the current instance.
+		/// </remarks>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			foreach (TagLib.Tag t in Tags) {
+				if (t is TagLib.Ape.Tag)
+					data.Add ((t as TagLib.Ape.Tag).Render ());
+				else if (t is TagLib.Id3v2.Tag)
+					data.Add ((t as TagLib.Id3v2.Tag).Render ());
+			}
+			
+			return data;
+		}
+		
+		/// <summary>
+		///    Writes the tags contained in the current instance to the
+		///    beginning of the file that created it, overwriting the
+		///    existing tags.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="long" /> value indicating the seek position
+		///    in the file at which the written tags end. This also
+		///    marks the seek position at which the media begins.
+		/// </returns>
+		public long Write ()
+		{
+			ByteVector data = Render ();
+			file.Insert (data, 0, TotalSize);
+			return data.Count;
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public void RemoveTags (TagTypes types)
+		{
+			for (int i = Tags.Length - 1; i >= 0; i--) {
+				var tag = Tags[i];
+				if (types == TagTypes.AllTags || (tag.TagTypes & types) == tag.TagTypes) {
+					RemoveTag (tag);
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Adds a tag of a specified type to the current instance,
+		///    optionally copying values from an existing type.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagTypes" /> value specifying the type of
+		///    tag to add to the current instance. At the time of this
+		///    writing, this is limited to <see cref="TagTypes.Ape" />
+		///    and <see cref="TagTypes.Id3v2" />.
+		/// </param>
+		/// <param name="copy">
+		///    A <see cref="TagLib.Tag" /> to copy values from using
+		///    <see cref="TagLib.Tag.CopyTo" />, or <see
+		///    langword="null" /> if no tag is to be copied.
+		/// </param>
+		/// <returns>
+		///    The <see cref="TagLib.Tag" /> object added to the current
+		///    instance, or <see langword="null" /> if it couldn't be
+		///    created.
+		/// </returns>
+		/// <remarks>
+		///    ID3v2 tags are added at the end of the current instance,
+		///    while other tags are added to the beginning.
+		/// </remarks>
+		public TagLib.Tag AddTag (TagTypes type, TagLib.Tag copy)
+		{
+			TagLib.Tag tag = null;
+			
+			if (type == TagTypes.Id3v2) {
+				tag = new TagLib.Id3v2.Tag ();
+			} else if (type == TagTypes.Ape) {
+				tag = new TagLib.Ape.Tag ();
+				(tag as Ape.Tag).HeaderPresent = true;
+			}
+			
+			if (tag != null) {
+				if (copy != null)
+					copy.CopyTo (tag, true);
+				
+				AddTag (tag);
+			}
+			
+			return tag;
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Reads a tag starting at a specified position and moves the
+		///    cursor to its start position.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value reference specifying at what
+		///    position the potential tag starts. If a tag is found,
+		///    this value will be updated to the position at which the
+		///    found tag ends.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Tag" /> object representing the tag
+		///    found at the specified position, or <see langword="null"
+		///    /> if no tag was found.
+		/// </returns>
+		private TagLib.Tag ReadTag (ref long start)
+		{
+			long end = start;
+			TagTypes type = ReadTagInfo (ref end);
+			TagLib.Tag tag = null;
+			
+			try {
+				switch (type)
+				{
+				case TagTypes.Ape:
+					tag = new TagLib.Ape.Tag (file, start);
+					break;
+				case TagTypes.Id3v2:
+					tag = new TagLib.Id3v2.Tag (file, start);
+					break;
+				}
+			} catch (CorruptFileException e) {
+                Console.Error.WriteLine ("taglib-sharp caught exception creating tag: {0}", e);
+			}
+
+			start = end;
+			return tag;
+		}
+		
+		/// <summary>
+		///    Looks for a tag starting at a specified position and moves
+		///    the cursor to its start position.
+		/// </summary>
+		/// <param name="position">
+		///    A <see cref="long" /> value reference specifying at what
+		///    position the potential tag starts. If a tag is found,
+		///    this value will be updated to the position at which the
+		///    found tag ends.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.TagTypes" /> value specifying the
+		///    type of tag found at the specified position, or <see
+		///    cref="TagTypes.None" /> if no tag was found.
+		/// </returns>
+		private TagTypes ReadTagInfo (ref long position)
+		{
+			file.Seek (position);
+			ByteVector data = file.ReadBlock (read_size);
+			
+			try {
+				if (data.StartsWith (TagLib.Ape.Footer.FileIdentifier)) {
+					TagLib.Ape.Footer footer =
+						new TagLib.Ape.Footer (data);
+					
+					position += footer.CompleteTagSize;
+					return TagTypes.Ape;
+				}
+				
+				if (data.StartsWith (TagLib.Id3v2.Header.FileIdentifier)) {
+					TagLib.Id3v2.Header header =
+						new TagLib.Id3v2.Header (data);
+					
+					position += header.CompleteTagSize;
+					return TagTypes.Id3v2;
+				}
+			} catch (CorruptFileException) {
+			}
+			
+			return TagTypes.None;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/NonContainer/Tag.cs b/lib/TagLib/TagLib/NonContainer/Tag.cs
new file mode 100644
index 0000000..1fff1e1
--- /dev/null
+++ b/lib/TagLib/TagLib/NonContainer/Tag.cs
@@ -0,0 +1,266 @@
+//
+// Tag.cs: Combines StartTag and EndTag in such a way as their children appear
+// as its children.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.NonContainer {
+	/// <summary>
+	///    This class extends <see cref="CombinedTag" />, combining <see
+	///    cref="StartTag" /> and <see cref="EndTag" /> in such a way as
+	///    their children appear as its children.
+	/// </summary>
+	public class Tag : CombinedTag
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the tags appearing at the start of the file.
+		/// </summary>
+		private StartTag start_tag;
+		
+		/// <summary>
+		///    Contains the tags appearing at the end of the file.
+		/// </summary>
+		private EndTag end_tag;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Tag" /> for a specified <see cref="TagLib.File" />.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object on which the new
+		///    instance will perform its operations.
+		/// </param>
+		/// <remarks>
+		///    Constructing a new instance does not automatically read
+		///    the contents from the disk. <see cref="Read" /> must be
+		///    called to read the tags.
+		/// </remarks>
+		public Tag (File file) : base ()
+		{
+			start_tag = new StartTag (file);
+			end_tag = new EndTag (file);
+			AddTag (start_tag);
+			AddTag (end_tag);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the collection of tags appearing at the start of the
+		///    file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.NonContainer.StartTag" /> storing the
+		///    tags for the start of the file.
+		/// </value>
+		public StartTag StartTag {
+			get {return start_tag;}
+		}
+		
+		/// <summary>
+		///    Gets the collection of tags appearing at the end of the
+		///    file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.NonContainer.EndTag" /> storing the
+		///    tags for the end of the file.
+		/// </value>
+		public EndTag EndTag {
+			get {return end_tag;}
+		}
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" />
+		///    containing the tag types contained in the current
+		///    instance.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return start_tag.TagTypes | end_tag.TagTypes;}
+		}
+		
+		/// <summary>
+		///    Gets the tags combined in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="Tag[]" /> containing the tags combined in
+		///    the current instance.
+		/// </value>
+		/// <remarks>
+		///    This contains the combined children of <see
+		///    cref="Tag.StartTag" /> and <see cref="Tag.EndTag" />.
+		/// </remarks>
+		public override TagLib.Tag [] Tags {
+			get {
+				List<TagLib.Tag> tags = new List<TagLib.Tag> ();
+				tags.AddRange (start_tag.Tags);
+				tags.AddRange (end_tag.Tags);
+				return tags.ToArray ();
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public TagLib.Tag GetTag (TagTypes type)
+		{
+			foreach (TagLib.Tag t in Tags) {
+				if (type == TagTypes.Id3v1 && t is TagLib.Id3v1.Tag)
+					return t;
+				
+				if (type == TagTypes.Id3v2 && t is TagLib.Id3v2.Tag)
+					return t;
+				
+				if (type == TagTypes.Ape && t is TagLib.Ape.Tag)
+					return t;
+			}
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public void RemoveTags (TagTypes types)
+		{
+			start_tag.RemoveTags (types);
+			end_tag.RemoveTags (types);
+		}
+		
+		/// <summary>
+		///    Reads the tags at the start and end of the file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value reference which will be set
+		///    to contain the seek position in the file at which the
+		///    tags at the start end. This also marks the seek position
+		///    at which the media begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value reference which will be set
+		///    to contain the seek position in the file at which the
+		///    tags at the end begin. This also marks the seek position
+		///    at which the media ends.
+		/// </param>
+		public void Read (out long start, out long end)
+		{
+			start = ReadStart ();
+			end = ReadEnd ();
+		}
+		
+		/// <summary>
+		///    Reads the tags stored at the start of the file into the
+		///    current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="long" /> value indicating the seek position
+		///    in the file at which the read tags end. This also
+		///    marks the seek position at which the media begins.
+		/// </returns>
+		public long ReadStart ()
+		{
+			return start_tag.Read ();
+		}
+		
+		/// <summary>
+		///    Reads the tags stored at the end of the file into the
+		///    current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="long" /> value indicating the seek position
+		///    in the file at which the read tags begin. This also
+		///    marks the seek position at which the media ends.
+		/// </returns>
+		public long ReadEnd ()
+		{
+			return end_tag.Read ();
+		}
+		
+		/// <summary>
+		///    Writes the tags to the start and end of the file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value reference which will be set
+		///    to contain the new seek position in the file at which the
+		///    tags at the start end. This also marks the seek position
+		///    at which the media begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value reference which will be set
+		///    to contain the new seek position in the file at which the
+		///    tags at the end begin. This also marks the seek position
+		///    at which the media ends.
+		/// </param>
+		public void Write (out long start, out long end)
+		{
+			start = start_tag.Write ();
+			end = end_tag.Write ();
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/Bitstream.cs b/lib/TagLib/TagLib/Ogg/Bitstream.cs
new file mode 100644
index 0000000..698d426
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/Bitstream.cs
@@ -0,0 +1,227 @@
+//
+// Bitstream.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    This class accepts a sequence of pages belonging to a single
+	///    logical bitstream, processes them, and extracts the tagging and
+	///    media information.
+	/// </summary>
+	public class Bitstream
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the last packet of the previous page in case it
+		///    is continued in the next frame.
+		/// </summary>
+		private ByteVector previous_packet;
+		
+		/// <summary>
+		///    Contains the index of the next packet to be processed.
+		/// </summary>
+		private int packet_index;
+		
+		/// <summary>
+		///    Contains the codec object used to process pages.
+		/// </summary>
+		private Codec codec;
+		
+		/// <summary>
+		///    Contains the absolute granular position of the first
+		///    page.
+		/// </summary>
+		private long first_absolute_granular_position;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Bitstream" /> capable of processing a specified
+		///    page.
+		/// </summary>
+		/// <param name="page">
+		///    The first <see cref="Page" /> object of the stream to be
+		///    processed by the new instance.
+		/// </param>
+		/// <remarks>
+		///    The constructor only sets the new instance up to read the
+		///    packet, but doesn't actually read it.
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="page" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="UnsupportedFormatException">
+		///    No registered codec capable of processing <paramref
+		///    name="page" /> could be found.
+		/// </exception>
+		public Bitstream (Page page)
+		{
+			if (page == null)
+				throw new ArgumentNullException ("page");
+			
+			// Assume that the first packet is completely enclosed.
+			// This should be sufficient for codec recognition.
+			codec = Codec.GetCodec (page.Packets [0]);
+
+			first_absolute_granular_position =
+				page.Header.AbsoluteGranularPosition;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Reads the next logical page in the stream.
+		/// </summary>
+		/// <param name="page">
+		///    The next logical <see cref="Page" /> object in the
+		///    stream.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the codec has read all the
+		///    necessary packets for the stream and does not need to be
+		///    called again, typically once the Xiph comment has been
+		///    found. Otherwise <see langword="false" />.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="page" /> is <see langword="null" />.
+		/// </exception>
+		public bool ReadPage (Page page)
+		{
+			if (page == null)
+				throw new ArgumentNullException ("page");
+			
+			ByteVector[] packets = page.Packets;
+			
+			for (int i = 0; i < packets.Length; i ++) {
+				if ((page.Header.Flags &
+					PageFlags.FirstPacketContinued) == 0 &&
+					previous_packet != null) {
+					if (ReadPacket (previous_packet))
+						return true;
+					previous_packet = null;
+				}
+				
+				
+				ByteVector packet = packets [i];
+				
+				// If we're at the first packet of the page, and
+				// we're continuing an old packet, combine the
+				// old with the new.
+				if (i == 0 && (page.Header.Flags &
+					PageFlags.FirstPacketContinued) != 0 &&
+					previous_packet != null) {
+					previous_packet.Add (packet);
+					packet = previous_packet;
+				}
+				
+				previous_packet = null;
+				
+				if (i == packets.Length - 1) {
+					// If we're at the last packet of the
+					// page, store it.
+					previous_packet = new ByteVector (packet);
+				} else if (ReadPacket (packet)) {
+					// Otherwise, we need to process it.
+					return true;
+				}
+			}
+			
+			return false;
+		}
+		
+		/// <summary>
+		///    Gets the duration of the stream represented by the
+		///    current instance.
+		/// </summary>
+		/// <param name="lastAbsoluteGranularPosition">
+		///    A <see cref="long" /> value containing the absolute
+		///    granular position of the last page in the bitstream.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TimeSpan" /> object containing the duration
+		///    of the stream represented by the current instance.
+		/// </returns>
+		public TimeSpan GetDuration (long lastAbsoluteGranularPosition)
+		{
+			return codec.GetDuration (
+				first_absolute_granular_position,
+				lastAbsoluteGranularPosition);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the codec object used to interpret the stream
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    The <see cref="Codec" /> object used by the current
+		///    instance.
+		/// </value>
+		public Codec Codec {
+			get {return codec;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Sents a packet to the codec processor to read it.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing the next
+		///    packet in the stream.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the codec has read all the
+		///    necessary packets for the stream and does not need to be
+		///    called again, typically once the Xiph comment has been
+		///    found. Otherwise <see langword="false" />.
+		/// </returns>
+		private bool ReadPacket (ByteVector packet)
+		{
+			return codec.ReadPacket (packet, packet_index++);
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/Codec.cs b/lib/TagLib/TagLib/Ogg/Codec.cs
new file mode 100644
index 0000000..7d7e01f
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/Codec.cs
@@ -0,0 +1,287 @@
+//
+// Codec.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    This abstract class implements <see cref="ICodec" /> to provide
+	///    support for processing packets from an Ogg logical bitstream.
+	/// </summary>
+	/// <remarks>
+	///    Unsupported Ogg codecs can be added by creating child classes and
+	///    registering them using <see cref="AddCodecProvider" />.
+	/// </remarks>
+	/// <seealso cref="CodecProvider" />
+	/// <seealso cref="AddCodecProvider" />
+	public abstract class Codec : ICodec
+	{
+#region Public Delegates
+		
+		/// <summary>
+		///    Represents a method capable of checking an Ogg header
+		///    packet to see it is matches a given codec.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing an Ogg
+		///    header packet.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Codec" /> object capable of processing the
+		///    stream from which the packet was generated; otherwise
+		///    <see langword="null" />.
+		/// </returns>
+		/// <remarks>
+		///    <para>In order to add support for a new Ogg codec in
+		///    TagLib#, a derivative class of <see cref="Codec" /> needs
+		///    to be created. The class then needs to be added to the
+		///    codec detection system by creating a <see
+		///    cref="CodecProvider" /> and registering it with <see
+		///    cref="AddCodecProvider"/>.</para>
+		///    <para>A method implementing <see cref="CodecProvider" />
+		///    should read <paramref name="packet" /> to determine if
+		///    it's subclass can handle the data. If it can, it should
+		///    return a new instance of that class, but in no way act
+		///    upon the data. If the class cannot be used to read the
+		///    packet, <see langref="null" /> indicates to the system
+		///    that it needs to try anther codec provider.</para>
+		/// </remarks>
+		/// <example>
+		///    <para>The following example would check for a Speex
+		///    packet and return a Speex codec:</para>
+		///    <code lang="C++">
+		/// Codec.AddCodecProvider (delegate (ByteVector packet) {
+		/// 	return packet.StartsWith ("Speex   ") ? new MySpeexCodec () : null;
+		/// });
+		///    </code>
+		/// </example>
+		public delegate Codec CodecProvider (ByteVector packet);
+		
+#endregion
+		
+		
+		
+#region Private Static Fields
+		
+		/// <summary>
+		///    Contains registered codec providers.
+		/// </summary>
+		private static List<CodecProvider> providers =
+			new List<CodecProvider> ();
+		
+#endregion
+		
+		
+		
+#region Private Static Methods
+		
+		/// <summary>
+		///    Determines the correct codec to use for a stream header
+		///    packet.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing the first
+		///    packet of an Ogg logical bitstream.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Codec" /> object capable of handling
+		///    <paramref name="packet" /> and subsequent packets from
+		///    the same stream.
+		/// </returns>
+		/// <exception cref="UnsupportedFormatException">
+		///    No registered codec capable of processing <paramref
+		///    name="packet" /> could be found.
+		/// </exception>
+		/// <remarks>
+		///    This method will first use <see cref="CodecProvider" />
+		///    delegates registered with <see cref="AddCodecProvider" />
+		///    and then attempt to use the built-in codecs.
+		/// </remarks>
+		public static Codec GetCodec (ByteVector packet)
+		{
+			Codec c = null;
+			
+			foreach (CodecProvider p in providers) {
+				c = p (packet);
+				if (c != null) return c;
+			}
+			
+			c = Codecs.Vorbis.FromPacket (packet);
+			if (c != null)
+				return c;
+			
+			c = Codecs.Theora.FromPacket (packet);
+			if (c != null)
+				return c;
+			
+			throw new UnsupportedFormatException ("Unknown codec.");
+		}
+		
+		/// <summary>
+		///    Adds a codec 
+		/// </summary>
+		/// <param name="provider">
+		/// A <see cref="CodecProvider"/>
+		/// </param>
+		/// <remarks>
+		///    A <see cref="CodecProvider" /> delegate is used to add
+		///    support for new <see cref="Codec" /> subclasses in <see
+		///    cref="GetCodec" />.
+		/// </remarks>
+		/// <seealso cref="CodecProvider" />
+		public static void AddCodecProvider (CodecProvider provider)
+		{
+			providers.Insert (0, provider);
+		}
+		
+#endregion
+		
+		
+		
+#region Private Properties
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public abstract string Description {get;}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="MediaTypes" /> containing
+		///    the types of media represented by the current instance.
+		/// </value>
+		public abstract MediaTypes MediaTypes {get;}
+		
+		/// <summary>
+		///    Gets the raw Xiph comment data contained in the codec.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing a raw Xiph
+		///    comment or <see langword="null"/> if none was found.
+		/// </value>
+		public abstract ByteVector CommentData {get;}
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TimeSpan.Zero" />.
+		/// </value>
+		/// <remarks>
+		///    In order to determine the duration of an Ogg stream, the
+		///    first and last granular positions will be passed to <see
+		///    cref="GetDuration" />.
+		/// </remarks>
+		public TimeSpan Duration {
+			get {return TimeSpan.Zero;}
+		}
+		
+#endregion
+		
+		
+		
+#region Private Methods
+		
+		/// <summary>
+		///    Reads a Ogg packet that has been encountered in the
+		///    stream.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing a packet to
+		///    be read by the current instance.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value containing the index of the
+		///    packet in the stream.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the codec has read all the
+		///    necessary packets for the stream and does not need to be
+		///    called again, typically once the Xiph comment has been
+		///    found. Otherwise <see langword="false" />.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="packet" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="index" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The data does not conform to the specificiation for the
+		///    codec represented by the current instance.
+		/// </exception>
+		public abstract bool ReadPacket (ByteVector packet, int index);
+		
+		/// <summary>
+		///    Computes the duration of the stream using the first and
+		///    last granular positions of the stream.
+		/// </summary>
+		/// <param name="firstGranularPosition">
+		///    A <see cref="long" /> value containing the first granular
+		///    position of the stream.
+		/// </param>
+		/// <param name="lastGranularPosition">
+		///    A <see cref="long" /> value containing the last granular
+		///    position of the stream.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TimeSpan" /> value containing the duration
+		///    of the stream.
+		/// </returns>
+		public abstract TimeSpan GetDuration (long firstGranularPosition,
+		                                      long lastGranularPosition);
+		
+		/// <summary>
+		///    Replaces the comment packet in a collection of packets
+		///    with the rendered version of a Xiph comment or inserts a
+		///    comment packet if the stream lacks one.
+		/// </summary>
+		/// <param name="packets">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    a collection of packets.
+		/// </param>
+		/// <param name="comment">
+		///    A <see cref="XiphComment" /> object to store the rendered
+		///    version of in <paramref name="packets" />.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="packets" /> or <paramref name="comment"
+		///    /> is <see langword="null" />.
+		/// </exception>
+		public abstract void SetCommentPacket (ByteVectorCollection packets,
+		                                       XiphComment comment);
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/Codecs/Theora.cs b/lib/TagLib/TagLib/Ogg/Codecs/Theora.cs
new file mode 100644
index 0000000..e2eab5e
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/Codecs/Theora.cs
@@ -0,0 +1,381 @@
+//
+// Theora.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Ogg.Codecs
+{
+	/// <summary>
+	///    This class extends <see cref="Codec" /> and implements <see
+	///    cref="IVideoCodec" /> to provide support for processing Ogg
+	///    Theora bitstreams.
+	/// </summary>
+	public class Theora : Codec, IVideoCodec
+	{
+#region Private Static Fields
+		
+		/// <summary>
+		///    Contains the file identifier.
+		/// </summary>
+		private static ByteVector id = "theora";
+		
+#endregion
+		
+		
+		
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the header packet.
+		/// </summary>
+		private HeaderPacket header;
+		
+		/// <summary>
+		///    Contains the comment data.
+		/// </summary>
+		private ByteVector comment_data;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Theora" />.
+		/// </summary>
+		private Theora ()
+		{
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Reads a Ogg packet that has been encountered in the
+		///    stream.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing a packet to
+		///    be read by the current instance.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value containing the index of the
+		///    packet in the stream.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the codec has read all the
+		///    necessary packets for the stream and does not need to be
+		///    called again, typically once the Xiph comment has been
+		///    found. Otherwise <see langword="false" />.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="packet" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="index" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The data does not conform to the specificiation for the
+		///    codec represented by the current instance.
+		/// </exception>
+		public override bool ReadPacket (ByteVector packet, int index)
+		{
+			if (packet == null)
+				throw new ArgumentNullException ("packet");
+			
+			if (index < 0)
+				throw new ArgumentOutOfRangeException ("index",
+					"index must be at least zero.");
+			
+			int type = PacketType (packet);
+			if (type != 0x80 && index == 0)
+				throw new CorruptFileException (
+					"Stream does not begin with theora header.");
+			
+			if (comment_data == null) {
+				if (type == 0x80)
+					header = new HeaderPacket (packet);
+				else if (type == 0x81)
+					comment_data = packet.Mid (7);
+				else
+					return true;
+			}
+			
+			return comment_data != null;
+		}
+		
+		/// <summary>
+		///    Computes the duration of the stream using the first and
+		///    last granular positions of the stream.
+		/// </summary>
+		/// <param name="firstGranularPosition">
+		///    A <see cref="long" /> value containing the first granular
+		///    position of the stream.
+		/// </param>
+		/// <param name="lastGranularPosition">
+		///    A <see cref="long" /> value containing the last granular
+		///    position of the stream.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TimeSpan" /> value containing the duration
+		///    of the stream.
+		/// </returns>
+		public override TimeSpan GetDuration (long firstGranularPosition,
+		                                      long lastGranularPosition)
+		{
+			return TimeSpan.FromSeconds (
+				header.GranuleTime (lastGranularPosition) -
+				header.GranuleTime (firstGranularPosition));
+		}
+		
+		/// <summary>
+		///    Replaces the comment packet in a collection of packets
+		///    with the rendered version of a Xiph comment or inserts a
+		///    comment packet if the stream lacks one.
+		/// </summary>
+		/// <param name="packets">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    a collection of packets.
+		/// </param>
+		/// <param name="comment">
+		///    A <see cref="XiphComment" /> object to store the rendered
+		///    version of in <paramref name="packets" />.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="packets" /> or <paramref name="comment"
+		///    /> is <see langword="null" />.
+		/// </exception>
+		public override void SetCommentPacket (ByteVectorCollection packets,
+		                                       XiphComment comment)
+		{
+			if (packets == null)
+				throw new ArgumentNullException ("packets");
+			
+			if (comment == null)
+				throw new ArgumentNullException ("comment");
+			
+			ByteVector data = new ByteVector ((byte) 0x81);
+			data.Add (id);
+			data.Add (comment.Render (true));
+			
+			if (packets.Count > 1 && PacketType (packets [1]) == 0x81)
+				packets [1] = data;
+			else
+				packets.Insert (1, data);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the width of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the width of the
+		///    video represented by the current instance.
+		/// </value>
+		public int VideoWidth {
+			get {return header.width;}
+		}
+		
+		/// <summary>
+		///    Gets the height of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the height of the
+		///    video represented by the current instance.
+		/// </value>
+		public int VideoHeight {
+			get {return header.height;}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Video" />.
+		/// </value>
+		public override MediaTypes MediaTypes {
+			get {return MediaTypes.Video;}
+		}
+		
+		/// <summary>
+		///    Gets the raw Xiph comment data contained in the codec.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing a raw Xiph
+		///    comment or <see langword="null"/> if none was found.
+		/// </value>
+		public override ByteVector CommentData {
+			get {return comment_data;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public override string Description {
+			get {
+				return string.Format (
+					"Theora Version {0}.{1} Video",
+					header.major_version,
+					header.minor_version);
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Implements the <see cref="CodecProvider" /> delegate to
+		///    provide support for recognizing a Theora stream from the
+		///    header packet.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing the stream
+		///    header packet.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Codec"/> object containing a codec capable
+		///    of parsing the stream of <see langref="null" /> if the
+		///    stream is not a Theora stream.
+		/// </returns>
+		public static Codec FromPacket (ByteVector packet)
+		{
+			return (PacketType (packet) == 0x80) ? new Theora () : null;
+		}
+		
+#endregion
+		
+		
+		
+#region Private Static Methods
+		
+		/// <summary>
+		///    Gets the packet type for a specified Theora packet.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing a Theora
+		///    packet.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value containing the packet type or
+		///    -1 if the packet is invalid.
+		/// </returns>
+		private static int PacketType (ByteVector packet)
+		{
+			if (packet.Count <= id.Count || packet [0] < 0x80)
+				return -1;
+			
+			for (int i = 0; i < id.Count; i ++)
+				if (packet [i + 1] != id [i])
+				return -1;
+			
+			return packet [0];
+		}
+		
+#endregion
+		
+		/// <summary>
+		///    This structure represents a Theora header packet.
+		/// </summary>
+		private struct HeaderPacket
+		{
+			public byte major_version;
+			public byte minor_version;
+			public byte revision_version;
+			public int width;
+			public int height;
+			public int fps_numerator;
+			public int fps_denominator;
+			public int keyframe_granule_shift;
+			
+			public HeaderPacket (ByteVector data)
+			{
+				major_version = data [7];
+				minor_version = data [8];
+				revision_version = data [9];
+				// width = data.Mid (10, 2).ToShort () << 4;
+				// height = data.Mid (12, 2).ToShort () << 4;
+				width = (int) data.Mid (14, 3).ToUInt (); // Frame Width.
+				height = (int) data.Mid (17, 3).ToUInt (); // Frame Height.
+				// Offset X.
+				// Offset Y.
+				fps_numerator = (int) data.Mid (22, 4).ToUInt ();
+				fps_denominator = (int) data.Mid (26, 4).ToUInt ();
+				// Aspect Numerator.
+				// Aspect Denominator.
+				// Colorspace.
+				// Target bitrate.
+				ushort last_bits = data.Mid (40, 2).ToUShort ();
+				keyframe_granule_shift = (last_bits >> 5) & 0x1F;
+			}
+			
+			/// <summary>
+			///    Converts an absolute granular position into a
+			///    seconds.
+			/// </summary>
+			/// <param name="granularPosition">
+			///    A <see cref="long" /> value containing the
+			///   absolute granular position.
+			/// </param>
+			/// <returns>
+			///    A <see cref="double" /> value containing the time
+			///    at <paramref name="granularPosition" /> in
+			///    seconds.</returns>
+			/// <remarks>
+			///    Many thanks to the good people at
+			///    irc://irc.freenode.net#theora for making this
+			///    code a reality.
+			/// </remarks>
+			public double GranuleTime (long granularPosition)
+			{
+				long iframe = granularPosition >>
+					keyframe_granule_shift;
+				long pframe = granularPosition -
+					(iframe << keyframe_granule_shift);
+				return (iframe + pframe) *
+					((double) fps_denominator /
+					(double) fps_numerator);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Ogg/Codecs/Vorbis.cs b/lib/TagLib/TagLib/Ogg/Codecs/Vorbis.cs
new file mode 100644
index 0000000..3491d44
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/Codecs/Vorbis.cs
@@ -0,0 +1,356 @@
+//
+// Vorbis.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Ogg.Codecs
+{
+	/// <summary>
+	///    This class extends <see cref="Codec" /> and implements <see
+	///    cref="IAudioCodec" /> to provide support for processing Ogg
+	///    Vorbis bitstreams.
+	/// </summary>
+	public class Vorbis : Codec, IAudioCodec
+	{
+#region Private Static Fields
+		
+		/// <summary>
+		///    Contains the file identifier.
+		/// </summary>
+		private static ByteVector id = "vorbis";
+		
+#endregion
+		
+		
+		
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the header packet.
+		/// </summary>
+		private HeaderPacket header;
+		
+		/// <summary>
+		///    Contains the comment data.
+		/// </summary>
+		private ByteVector comment_data;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Vorbis" />.
+		/// </summary>
+		private Vorbis ()
+		{
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Reads a Ogg packet that has been encountered in the
+		///    stream.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing a packet to
+		///    be read by the current instance.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value containing the index of the
+		///    packet in the stream.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the codec has read all the
+		///    necessary packets for the stream and does not need to be
+		///    called again, typically once the Xiph comment has been
+		///    found. Otherwise <see langword="false" />.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="packet" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="index" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The data does not conform to the specificiation for the
+		///    codec represented by the current instance.
+		/// </exception>
+		public override bool ReadPacket (ByteVector packet, int index)
+		{
+			if (packet == null)
+				throw new ArgumentNullException ("packet");
+			
+			if (index < 0)
+				throw new ArgumentOutOfRangeException ("index",
+					"index must be at least zero.");
+			
+			int type = PacketType (packet);
+			if (type != 1 && index == 0)
+				throw new CorruptFileException (
+					"Stream does not begin with vorbis header.");
+			
+			if (comment_data == null) {
+				if (type == 1)
+					header = new HeaderPacket (packet);
+				else if (type == 3)
+					comment_data = packet.Mid (7);
+				else
+					return true;
+			}
+			
+			return comment_data != null;
+		}
+		
+		/// <summary>
+		///    Computes the duration of the stream using the first and
+		///    last granular positions of the stream.
+		/// </summary>
+		/// <param name="firstGranularPosition">
+		///    A <see cref="long" /> value containing the first granular
+		///    position of the stream.
+		/// </param>
+		/// <param name="lastGranularPosition">
+		///    A <see cref="long" /> value containing the last granular
+		///    position of the stream.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TimeSpan" /> value containing the duration
+		///    of the stream.
+		/// </returns>
+		public override TimeSpan GetDuration (long firstGranularPosition,
+		                                      long lastGranularPosition)
+		{
+			return header.sample_rate == 0 ? TimeSpan.Zero : 
+				TimeSpan.FromSeconds ((double)
+					(lastGranularPosition -
+						firstGranularPosition) /
+					(double) header.sample_rate);
+		}
+		
+		/// <summary>
+		///    Replaces the comment packet in a collection of packets
+		///    with the rendered version of a Xiph comment or inserts a
+		///    comment packet if the stream lacks one.
+		/// </summary>
+		/// <param name="packets">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    a collection of packets.
+		/// </param>
+		/// <param name="comment">
+		///    A <see cref="XiphComment" /> object to store the rendered
+		///    version of in <paramref name="packets" />.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="packets" /> or <paramref name="comment"
+		///    /> is <see langword="null" />.
+		/// </exception>
+		public override void SetCommentPacket (ByteVectorCollection packets,
+		                                       XiphComment comment)
+		{
+			if (packets == null)
+				throw new ArgumentNullException ("packets");
+			
+			if (comment == null)
+				throw new ArgumentNullException ("comment");
+			
+			ByteVector data = new ByteVector ((byte) 0x03);
+			data.Add (id);
+			data.Add (comment.Render (true));
+			if (packets.Count > 1 && PacketType (packets [1]) == 0x03)
+				packets [1] = data;
+			else
+				packets.Insert (1, data);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				return (int) ((float)header.bitrate_nominal /
+					1000f + 0.5);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {return (int) header.sample_rate;}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return (int) header.channels;}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public override MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets the raw Xiph comment data contained in the codec.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing a raw Xiph
+		///    comment or <see langword="null"/> if none was found.
+		/// </value>
+		public override ByteVector CommentData {
+			get {return comment_data;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public override string Description {
+			get {
+				return string.Format (
+					"Vorbis Version {0} Audio",
+					header.vorbis_version);
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Implements the <see cref="CodecProvider" /> delegate to
+		///    provide support for recognizing a Vorbis stream from the
+		///    header packet.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing the stream
+		///    header packet.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Codec"/> object containing a codec capable
+		///    of parsing the stream of <see langref="null" /> if the
+		///    stream is not a Vorbis stream.
+		/// </returns>
+		public static Codec FromPacket (ByteVector packet)
+		{
+			return (PacketType (packet) == 1) ? new Vorbis () : null;
+		}
+		
+#endregion
+		
+		
+		
+#region Private Static Methods
+		
+		/// <summary>
+		///    Gets the packet type for a specified Vorbis packet.
+		/// </summary>
+		/// <param name="packet">
+		///    A <see cref="ByteVector" /> object containing a Vorbis
+		///    packet.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value containing the packet type or
+		///    -1 if the packet is invalid.
+		/// </returns>
+		private static int PacketType (ByteVector packet)
+		{
+			if (packet.Count <= id.Count)
+				return -1;
+			
+			for (int i = 0; i < id.Count; i ++)
+				if (packet [i + 1] != id [i])
+					return -1;
+			
+			return packet [0];
+		}
+		
+#endregion
+		
+		/// <summary>
+		///    This structure represents a Vorbis header packet.
+		/// </summary>
+		private struct HeaderPacket
+		{
+			public uint sample_rate;
+			public uint channels;
+			public uint vorbis_version;
+			public uint bitrate_maximum;
+			public uint bitrate_nominal;
+			public uint bitrate_minimum;
+			
+			public HeaderPacket (ByteVector data)
+			{
+				vorbis_version  = data.Mid(7, 4).ToUInt (false);
+				channels        = data [11];
+				sample_rate     = data.Mid(12, 4).ToUInt (false);
+				bitrate_maximum = data.Mid(16, 4).ToUInt (false);
+				bitrate_nominal = data.Mid(20, 4).ToUInt (false);
+				bitrate_minimum = data.Mid(24, 4).ToUInt (false);
+			}
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/File.cs b/lib/TagLib/TagLib/Ogg/File.cs
new file mode 100644
index 0000000..f85fc28
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/File.cs
@@ -0,0 +1,443 @@
+//
+// File.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   oggfile.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2003 Scott Wheeler (Original 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
+//
+
+using System.Collections.Generic;
+using System;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide tagging
+	///    and properties support for Ogg files.
+	/// </summary>
+	[SupportedMimeType("taglib/ogg", "ogg")]
+	[SupportedMimeType("taglib/oga", "oga")]
+	[SupportedMimeType("taglib/ogv", "ogv")]
+	[SupportedMimeType("application/ogg")]
+	[SupportedMimeType("application/x-ogg")]
+	[SupportedMimeType("audio/vorbis")]
+	[SupportedMimeType("audio/x-vorbis")]
+	[SupportedMimeType("audio/x-vorbis+ogg")]
+	[SupportedMimeType("audio/ogg")]
+	[SupportedMimeType("audio/x-ogg")]
+	[SupportedMimeType("video/ogg")]
+	[SupportedMimeType("video/x-ogm+ogg")]
+	[SupportedMimeType("video/x-theora+ogg")]
+	[SupportedMimeType("video/x-theora")]
+	public class File : TagLib.File
+	{
+#region Private Fields
+		
+		/// <summary>
+		///   Contains the tags for the file.
+		/// </summary>
+		private GroupedComment tag;
+		
+		/// <summary>
+		///    Contains the media properties.
+		/// </summary>
+		private Properties properties;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: this (new File.LocalFileAbstraction (path),
+				propertiesStyle)
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : this (path, ReadStyle.Average)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle) : base (abstraction)
+		{
+			Mode = AccessMode.Read;
+			try {
+				tag = new GroupedComment ();
+				Read (propertiesStyle);
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: this (abstraction, ReadStyle.Average)
+		{
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			Mode = AccessMode.Write;
+			try {
+				long end;
+				List<Page> pages = new List<Page> ();
+				Dictionary<uint, Bitstream> streams =
+					ReadStreams (pages, out end);
+				Dictionary<uint, Paginator> paginators =
+					new Dictionary<uint, Paginator> ();
+				List<List<Page>> new_pages =
+					new List<List<Page>> ();
+				Dictionary<uint, int> shifts =
+					new Dictionary<uint, int> ();
+				
+				foreach (Page page in pages) {
+					uint id = page.Header.StreamSerialNumber;
+					if (!paginators.ContainsKey (id))
+						paginators.Add (id,
+							new Paginator (
+								streams [id].Codec));
+					
+					paginators [id].AddPage (page);
+				}
+				
+				foreach (uint id in paginators.Keys) {
+					paginators [id].SetComment (
+						tag.GetComment (id));
+					int shift;
+					new_pages.Add (new List<Page> (
+						paginators [id]
+							.Paginate (out shift)));
+					shifts.Add (id, shift);
+				}
+				
+				ByteVector output = new ByteVector ();
+				bool empty;
+				do {
+					empty = true;
+					foreach (List<Page> stream_pages in new_pages) {
+						if (stream_pages.Count == 0)
+							continue;
+					
+					output.Add (stream_pages [0].Render ());
+					stream_pages.RemoveAt (0);
+					
+					if (stream_pages.Count != 0)
+						empty = false;
+					}
+				} while (!empty);
+				
+				Insert (output, 0, end);
+				InvariantStartPosition = output.Count;
+				InvariantEndPosition = Length;
+				
+				TagTypesOnDisk = TagTypes;
+				
+				Page.OverwriteSequenceNumbers (this,
+					output.Count, shifts);
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags (TagLib.TagTypes types)
+		{
+			if ((types & TagLib.TagTypes.Xiph)
+				!= TagLib.TagTypes.None)
+				tag.Clear ();
+		}
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public override TagLib.Tag GetTag (TagLib.TagTypes type,
+		                                   bool create)
+		{
+			if (type == TagLib.TagTypes.Xiph)
+				foreach (XiphComment comment in tag.Comments)
+					return comment;
+			
+			return null;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override Tag Tag {
+			get {return tag;}
+		}
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties {
+			get {return properties;}
+		}
+		
+#endregion
+		
+		
+		
+#region Private Methods
+		
+		/// <summary>
+		///    Reads the file with a specified read style.
+		/// </summary>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		private void Read (ReadStyle propertiesStyle)
+		{
+			long end;
+			Dictionary<uint, Bitstream> streams = ReadStreams (null,
+				out end);
+			List<ICodec> codecs = new List<ICodec> ();
+			InvariantStartPosition = end;
+			InvariantEndPosition = Length;
+			
+			foreach (uint id in streams.Keys) {
+				tag.AddComment (id,
+					streams [id].Codec.CommentData);
+				codecs.Add (streams [id].Codec);
+			}
+			
+			if (propertiesStyle == ReadStyle.None)
+				return;
+			
+			PageHeader last_header = LastPageHeader;
+			
+			TimeSpan duration = streams [last_header
+				.StreamSerialNumber].GetDuration (
+					last_header.AbsoluteGranularPosition);
+			properties = new Properties (duration, codecs);
+		}
+		
+		/// <summary>
+		///    Reads the file until all streams have finished their
+		///    property and tagging data.
+		/// </summary>
+		/// <param name="pages">
+		///    A <see cref="T:System.Collections.Generic.List`1"/>
+		///    object to be filled with <see cref="Page" /> objects as
+		///    they are read, or <see langword="null"/> if the pages
+		///    are not to be stored.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value reference to be updated to
+		///    the postion of the first page not read by the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="T:System.Collections.Generic.Dictionary`2"
+		///    /> object containing stream serial numbers as the keys
+		///    <see cref="Bitstream" /> objects as the values.
+		/// </returns>
+		private Dictionary<uint, Bitstream> ReadStreams (List<Page> pages,
+		                                                 out long end)
+		{
+			Dictionary<uint, Bitstream> streams =
+				new Dictionary<uint, Bitstream> ();
+			List<Bitstream> active_streams = new List<Bitstream> ();
+			
+			long position = 0;
+			
+			do {
+				Bitstream stream = null;
+				Page page = new Page (this, position);
+				
+				if ((page.Header.Flags &
+					PageFlags.FirstPageOfStream) != 0) {
+					stream = new Bitstream (page);
+					streams.Add (page.Header
+						.StreamSerialNumber, stream);
+					active_streams.Add (stream);
+				}
+				
+				if (stream == null)
+					stream = streams [
+						page.Header.StreamSerialNumber];
+				
+				if (active_streams.Contains (stream)
+					&& stream.ReadPage (page))
+					active_streams.Remove (stream);
+				
+				if (pages != null)
+					pages.Add (page);
+				
+				position += page.Size;
+			} while (active_streams.Count > 0);
+			
+			end = position;
+			
+			return streams;
+		}
+		
+#endregion
+		
+		
+		
+#region Private Properties
+		
+		/// <summary>
+		///    Gets the last page header in the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="PageHeader" /> object containing the last
+		///    page header in the file.
+		/// </value>
+		/// <remarks>
+		///    The last page header is used to determine the last
+		///    absolute granular position of a stream so the duration
+		///    can be calculated.
+		/// </remarks>
+		private PageHeader LastPageHeader {
+			get {
+				long last_page_header_offset = RFind ("OggS");
+				
+				if (last_page_header_offset < 0)
+					throw new CorruptFileException (
+						"Could not find last header.");
+				
+				return new PageHeader (this,
+					last_page_header_offset);
+			}
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/GroupedComment.cs b/lib/TagLib/TagLib/Ogg/GroupedComment.cs
new file mode 100644
index 0000000..e7b03f9
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/GroupedComment.cs
@@ -0,0 +1,1269 @@
+//
+// GroupedComment.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System.Collections.Generic;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    This class combines a collection of <see cref="XiphComment"/>
+	///    objects so that properties can be read from each but are only set
+	///    to the first comment of the file.
+	/// </summary>
+	public class GroupedComment : Tag
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains a mapping between stream serial numbers and
+		///    comments.
+		/// </summary>
+		private Dictionary<uint, XiphComment> comment_hash;
+		
+		/// <summary>
+		///    Contains comments in the order they are added.
+		/// </summary>
+		private List<XiphComment> tags;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="GroupedComment" /> with now contents.
+		/// </summary>
+		public GroupedComment () : base ()
+		{
+			comment_hash = new Dictionary <uint, XiphComment> ();
+			tags = new List<XiphComment> ();
+		}
+		
+		/// <summary>
+		///    Gets an enumeration of the comments in the current
+		///    instance, in the order they were added.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1"
+		///    /> object enumerating through the <see cref="XiphComment"
+		///    /> objects contained in the current instance.
+		/// </value>
+		public IEnumerable<XiphComment> Comments {
+			get {return tags;}
+		}
+		
+		/// <summary>
+		///    Gets a comment in the current instance for a specified
+		///    stream.
+		/// </summary>
+		/// <param name="streamSerialNumber">
+		///    A <see cref="uint" /> value containing the serial number
+		///    of the stream of the comment to get.
+		/// </param>
+		/// <returns>
+		///    A <see cref="XiphComment"/> with the matching serial
+		///    number.
+		/// </returns>
+		public XiphComment GetComment (uint streamSerialNumber)
+		{
+			return comment_hash [streamSerialNumber];
+		}
+		
+		/// <summary>
+		///    Adds a Xiph comment to the current instance.
+		/// </summary>
+		/// <param name="streamSerialNumber">
+		///    A <see cref="uint" /> value containing the serial number
+		///    of the stream containing the comment.
+		/// </param>
+		/// <param name="comment">
+		///    A <see cref="XiphComment" /> object to add to the current
+		///    instance.
+		/// </param>
+		public void AddComment (uint streamSerialNumber,
+		                        XiphComment comment)
+		{
+			comment_hash.Add (streamSerialNumber, comment);
+			tags.Add (comment);
+		}
+		
+		/// <summary>
+		///    Adds a Xiph comment to the current instance.
+		/// </summary>
+		/// <param name="streamSerialNumber">
+		///    A <see cref="uint" /> value containing the serial number
+		///    of the stream containing the comment.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> object containing the raw Xiph
+		///    comment to add to the current instance.
+		/// </param>
+		public void AddComment (uint streamSerialNumber,
+		                        ByteVector data)
+		{
+			AddComment (streamSerialNumber, new XiphComment (data));
+		}
+		
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" />
+		///    containing the tag types contained in the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    This value contains a bitwise combined value from all the
+		///    child tags.
+		/// </remarks>
+		/// <seealso cref="Tag.TagTypes" />
+		public override TagTypes TagTypes {
+			get {
+				TagTypes types = TagTypes.None;
+				foreach (XiphComment tag in tags)
+					if (tag != null)
+						types |= tag.TagTypes;
+				
+				return types;
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Title" />
+		public override string Title {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Title;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set { if (tags.Count > 0) tags[0].Title = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the individual track title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort name
+		///    for the track title of the media described by the current 
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.TitleSort" />
+		public override string TitleSort {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.TitleSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].TitleSort = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> array containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Performers" />
+		public override string[] Performers {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.Performers;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			set { if (tags.Count > 0) tags[0].Performers = value; }
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.PerformersSort" />
+		public override string [] PerformersSort {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.PerformersSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			set {if (tags.Count > 0) tags [0].PerformersSort = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> array containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AlbumArtists" />
+		public override string[] AlbumArtists {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.AlbumArtists;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			set { if (tags.Count > 0) tags[0].AlbumArtists = value; }
+		}
+
+		/// <summary>
+		///    Gets and sets the sort names for the band or artist who
+		///    is credited in the creation of the entire album or
+		///    collection containing the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the band or artist who is credited in the creation
+		///    of the entire album or collection containing the media
+		///    described by the current instance or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AlbumArtistsSort" />
+		public override string[] AlbumArtistsSort {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.AlbumArtistsSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			
+			set {if (tags.Count > 0) tags [0].AlbumArtistsSort = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> array containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Composers" />
+		public override string[] Composers {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.Composers;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			set { if (tags.Count > 0) tags[0].Composers = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the composer of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the composer of the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.ComposersSort" />
+		public override string [] ComposersSort {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.ComposersSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return new string[] { };
+			}
+			set {if (tags.Count > 0) tags [0].ComposersSort = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Album" />
+		public override string Album {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Album;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set { if (tags.Count > 0) tags[0].Album = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the album title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort names
+		///    for the album title of the media described by the
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AlbumSort" />
+		public override string AlbumSort {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.AlbumSort;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].AlbumSort = value;}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Comment" />
+		public override string Comment {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Comment;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set { if (tags.Count > 0) tags[0].Comment = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> array containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Genres" />
+		public override string[] Genres {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string[] value = tag.Genres;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+			
+				return new string[] { };
+			}
+			set { if (tags.Count > 0) tags[0].Genres = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Year" />
+		public override uint Year {
+			get {
+				foreach (XiphComment tag in tags)
+					if (tag != null && tag.Year != 0)
+						return tag.Year;
+				
+				return 0;
+			}
+			set { if (tags.Count > 0) tags[0].Year = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Track" />
+		public override uint Track {
+			get {
+				foreach (XiphComment tag in tags)
+					if (tag != null && tag.Track != 0)
+						return tag.Track;
+				
+				return 0;
+			}
+			set { if (tags.Count > 0) tags[0].Track = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.TrackCount" />
+		public override uint TrackCount {
+			get {
+				foreach (XiphComment tag in tags)
+					if (tag != null && tag.TrackCount != 0)
+						return tag.TrackCount;
+				
+				return 0;
+			}
+			set { if (tags.Count > 0) tags[0].TrackCount = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Disc" />
+		public override uint Disc {
+			get {
+				foreach (XiphComment tag in tags)
+					if (tag != null && tag.Disc != 0)
+						return tag.Disc;
+				
+				return 0;
+			}
+			set { if (tags.Count > 0) tags[0].Disc = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.DiscCount" />
+		public override uint DiscCount {
+			get {
+				foreach (XiphComment tag in tags)
+					if (tag != null && tag.DiscCount != 0)
+						return tag.DiscCount;
+				
+				return 0;
+			}
+			set { if (tags.Count > 0) tags[0].DiscCount = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Lyrics" />
+		public override string Lyrics {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Lyrics;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set { if (tags.Count > 0) tags[0].Lyrics = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Grouping" />
+		public override string Grouping {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Grouping;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set { if (tags.Count > 0) tags[0].Grouping = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-zero value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.BeatsPerMinute" />
+		public override uint BeatsPerMinute {
+			get {
+				foreach (XiphComment tag in tags)
+					if (tag != null && tag.BeatsPerMinute != 0)
+						return tag.BeatsPerMinute;
+				
+				return 0;
+				}
+			
+			set { if (tags.Count > 0) tags[0].BeatsPerMinute = value; }
+		}
+
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Conductor" />
+		public override string Conductor {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Conductor;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set { if (tags.Count > 0) tags[0].Conductor = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-<see
+		///    langword="null" /> value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Copyright" />
+		public override string Copyright {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.Copyright;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set { if (tags.Count > 0) tags[0].Copyright = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the MusicBrainz Artist ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ArtistID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzArtistId" />
+		public override string MusicBrainzArtistId {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzArtistId;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzArtistId = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseId" />
+		public override string MusicBrainzReleaseId {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseId;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzReleaseId = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Artist ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    ReleaseArtistID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseArtistId" />
+		public override string MusicBrainzReleaseArtistId {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseArtistId;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzReleaseArtistId = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Track ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    TrackID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzTrackId" />
+		public override string MusicBrainzTrackId {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzTrackId;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzTrackId = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Disc ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    DiscID for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzDiscId" />
+		public override string MusicBrainzDiscId {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzDiscId;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzDiscId = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicIP PUID
+		///    for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicIpId" />
+		public override string MusicIpId {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicIpId;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicIpId = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the Amazon ID.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the Amazon ID
+		///    for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.AmazonId" />
+		public override string AmazonId {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.AmazonId;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].AmazonId = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Status.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    Release Status for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseStatus" />
+		public override string MusicBrainzReleaseStatus {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseStatus;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzReleaseStatus = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Type.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    Release Type for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseType" />
+		public override string MusicBrainzReleaseType {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseType;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzReleaseType = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Country.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz
+		///    Release Country for the media described by the 
+		///    current instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are looped
+		///    through in order and the first non-<see langword="null" />
+		///    and non-empty value is returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.MusicBrainzReleaseCountry" />
+		public override string MusicBrainzReleaseCountry {
+			get {
+				foreach (XiphComment tag in tags) {
+					if (tag == null)
+						continue;
+					
+					string value = tag.MusicBrainzReleaseCountry;
+					
+					if (value != null && value.Length > 0)
+						return value;
+				}
+				
+				return null;
+			}
+			set {if (tags.Count > 0) tags [0].MusicBrainzReleaseCountry = value;}
+		}
+
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    <para>When getting the value, the child comments are
+		///    looped through in order and the first non-empty value is
+		///    returned.</para>
+		///    <para>When setting the value, it is stored in the first
+		///    comment.</para>
+		/// </remarks>
+		/// <seealso cref="Tag.Pictures" />
+		public override IPicture [] Pictures {
+			get {
+				IPicture [] output = new IPicture [0];
+				foreach (XiphComment tag in tags)
+					if (tag != null && output.Length == 0)
+						output = tag.Pictures;
+				
+				return output;
+			}
+			set {if (tags.Count > 0) tags [0].Pictures = value;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if all the comments tags are
+		///     empty; otherwise <see langword="false" />.
+		/// </value>
+		/// <seealso cref="Tag.IsEmpty" />
+		public override bool IsEmpty {
+			get {
+				foreach (XiphComment tag in tags)
+					if (!tag.IsEmpty)
+						return false;
+				
+				return true;
+			}
+		}
+		
+		/// <summary>
+		///    Clears all of the child tags.
+		/// </summary>
+		public override void Clear ()
+		{
+			foreach (XiphComment tag in tags)
+				tag.Clear ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/Page.cs b/lib/TagLib/TagLib/Ogg/Page.cs
new file mode 100644
index 0000000..40e1d68
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/Page.cs
@@ -0,0 +1,299 @@
+//
+// PageHeader.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   oggpage.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2003 Scott Wheeler (Original 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
+//
+
+using System.Collections.Generic;
+using System;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    This class provides a representation of an Ogg page.
+	/// </summary>
+	public class Page
+	{
+#region Private Properties
+		
+		/// <summary>
+		///    Contains the page header.
+		/// </summary>
+		private PageHeader header;
+		
+		/// <summary>
+		///    Contains the packets.
+		/// </summary>
+		private ByteVectorCollection packets;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and intializes a new instance of <see
+		///    cref="Page" /> with a specified header and no packets.
+		/// </summary>
+		/// <param name="header">
+		///    A <see cref="PageHeader"/> object to use as the header of
+		///    the new instance.
+		/// </param>
+		protected Page (PageHeader header)
+		{
+			this.header = header;
+			packets = new ByteVectorCollection ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Page" /> by reading a raw Ogg page from a specified
+		///    position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The Ogg identifier could not be found at the correct
+		///    location.
+		/// </exception>
+		public Page (File file, long position)
+			: this (new PageHeader (file, position))
+		{
+			file.Seek (position + header.Size);
+			
+			foreach (int packet_size in header.PacketSizes)
+				packets.Add (file.ReadBlock (packet_size));
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Page" /> with a specified header and packets.
+		/// </summary>
+		/// <param name="packets">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    packets to use for the new instance.
+		/// </param>
+		/// <param name="header">
+		///    A <see cref="PageHeader"/> object to use as the header of
+		///    the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="packets" /> is <see langword="null" />.
+		/// </exception>
+		public Page (ByteVectorCollection packets, PageHeader header)
+			: this (header)
+		{
+			if (packets == null)
+				throw new ArgumentNullException ("packets");
+			
+			this.packets = new ByteVectorCollection (packets);
+			
+			List<int> packet_sizes = new List<int> ();
+			
+			// Build a page from the list of packets.
+			foreach (ByteVector v in packets)
+				packet_sizes.Add (v.Count);
+			
+			header.PacketSizes = packet_sizes.ToArray ();
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw Ogg page.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector data = header.Render ();
+			
+			foreach (ByteVector v in packets)
+				data.Add (v);
+			
+			// 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;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the header of the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="PageHeader" /> object that applies to the
+		///    current instance.
+		/// </value>
+		public PageHeader Header {
+			get {return header;}
+		}
+		
+		/// <summary>
+		///    Gets the packets contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector[]" /> containing the packets
+		///    contained in the current instance.
+		/// </value>
+		public ByteVector[] Packets {
+			get {return packets.ToArray ();}
+		}
+		
+		/// <summary>
+		///    Gets the total size of the current instance as it
+		///    appeared on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the size of the
+		///    page, including the header, as it appeared on disk.
+		/// </value>
+		public uint Size {
+			get {return header.Size + header.DataSize;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Static Methods
+		
+		/// <summary>
+		///    Overwrites all page headers in a file starting at a
+		///    specified position, shifting the page sequence numbers
+		///    a set amount.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file to
+		///    update.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    start updating.
+		/// </param>
+		/// <param name="shiftTable">
+		///    A <see cref="T:System.Collections.Generic.IDictionary`2"
+		///    /> object where the key is the serial number of the
+		///    stream to update and the value is the amount to offset
+		///    the page sequence numbers in the stream.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> or <paramref name="shiftTable"
+		///    /> is <see langword="null" />.
+		/// </exception>
+		/// <remarks>
+		///    When the number of pages in a stream changes, all
+		///    subsequent pages in the stream need to have their page
+		///    sequence number update in order to remain valid.
+		///    Additionally, when the page sequence number changes, the
+		///    page needs to have its checksum recomputed. This makes
+		///    for a costly recalculation if large comment data is
+		///    added.
+		/// </remarks>
+		public static void OverwriteSequenceNumbers (File file,
+		                                             long position,
+		                                             IDictionary<uint, int> shiftTable)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (shiftTable == null)
+				throw new ArgumentNullException ("shiftTable");
+			
+			// Check to see if there are no changes to be made.
+			bool done = true;
+			foreach (KeyValuePair<uint, int> pair in shiftTable)
+				if (pair.Value != 0) {
+					done = false;
+					break;
+				}
+			
+			// If the file is fine, quit.
+			if (done)
+				return;
+			
+			while (position < file.Length - 27) {
+				PageHeader header = new PageHeader (file, position);
+				int size = (int) (header.Size + header.DataSize);
+				
+				if (shiftTable.ContainsKey (header.StreamSerialNumber)
+					&& shiftTable [header.StreamSerialNumber] != 0) {
+					file.Seek (position);
+					ByteVector page_data = file.ReadBlock (size);
+					
+					ByteVector new_data = ByteVector.FromUInt (
+						(uint)(header.PageSequenceNumber +
+						shiftTable [header.StreamSerialNumber]),
+						false);
+					
+					for (int i = 18; i < 22; i ++)
+						page_data [i] = new_data [i - 18];
+					for (int i = 22; i < 26; i++)
+						page_data [i] = 0;
+					
+					new_data.Add (ByteVector.FromUInt (
+						page_data.Checksum, false));
+					file.Seek (position + 18);
+					file.WriteBlock (new_data);
+				}
+				position += size;
+			}
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/PageHeader.cs b/lib/TagLib/TagLib/Ogg/PageHeader.cs
new file mode 100644
index 0000000..1a560b4
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/PageHeader.cs
@@ -0,0 +1,559 @@
+//
+// PageHeader.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   oggpageheader.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    Indicates the special properties of a <see cref="Page" />.
+	/// </summary>
+	[Flags]
+	public enum PageFlags : byte
+	{
+		/// <summary>
+		///    The page is a normal page.
+		/// </summary>
+		None = 0,
+		
+		/// <summary>
+		///    The first packet of the page is continued from the
+		///    previous page.
+		/// </summary>
+		FirstPacketContinued = 1,
+		
+		/// <summary>
+		///    The page is the first page of the stream.
+		/// </summary>
+		FirstPageOfStream = 2,
+		
+		/// <summary>
+		///    The page is the last page of the stream.
+		/// </summary>
+		LastPageOfStream = 4
+	}
+	
+	/// <summary>
+	///    This structure provides a representation of an Ogg page header.
+	/// </summary>
+	public struct PageHeader
+	{
+#region Private Propertis
+		
+		/// <summary>
+		///    Contains the sizes of the packets contained in the
+		///    current instance.
+		/// </summary>
+		private List<int> packet_sizes;
+		
+		/// <summary>
+		///    Contains the OGG version.
+		/// </summary>
+		private byte version;
+		
+		/// <summary>
+		///    Contains the page flags.
+		/// </summary>
+		private PageFlags flags;
+		
+		/// <summary>
+		///    Contains the page absolute granular postion.
+		/// </summary>
+		private ulong absolute_granular_position;
+		
+		/// <summary>
+		///    Contains the stream serial number of the page.
+		/// </summary>
+		private uint stream_serial_number;
+		
+		/// <summary>
+		///    Contains the page sequence number.
+		/// </summary>
+		private uint page_sequence_number;
+		
+		/// <summary>
+		///    Contains the header size on disk.
+		/// </summary>
+		private uint size;
+		
+		/// <summary>
+		///    Contains the data size on disk.
+		/// </summary>
+		private uint data_size;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PageHeader" /> with a given serial number, page
+		///    number, and flags.
+		/// </summary>
+		/// <param name="streamSerialNumber">
+		///    A <see cref="uint" /> value containing the serial number
+		///    for the stream containing the page described by the new
+		///    instance.
+		/// </param>
+		/// <param name="pageNumber">
+		///    A <see cref="uint" /> value containing the index of the
+		///    page described by the new instance in the stream.
+		/// </param>
+		/// <param name="flags">
+		///    A <see cref="PageFlags" /> object containing the flags
+		///    that apply to the page described by the new instance.
+		/// </param>
+		public PageHeader (uint streamSerialNumber, uint pageNumber,
+		                   PageFlags flags)
+		{
+			version = 0;
+			this.flags = flags;
+			absolute_granular_position = 0;
+			stream_serial_number = streamSerialNumber;
+			page_sequence_number = pageNumber;
+			size = 0;
+			data_size = 0;
+			packet_sizes = new List<int> ();
+			
+			if (pageNumber == 0 &&
+				(flags & PageFlags.FirstPacketContinued) == 0)
+				this.flags |= PageFlags.FirstPageOfStream;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PageHeader" /> by reading a raw Ogg page header
+		///    from a specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file from
+		///    which the contents of the new instance are to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The Ogg identifier could not be found at the correct
+		///    location.
+		/// </exception>
+		public PageHeader (File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (position < 0 || position > file.Length - 27)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			file.Seek (position);
+			
+			// 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 = file.ReadBlock (27);
+			if (data.Count < 27 || !data.StartsWith ("OggS"))
+				throw new CorruptFileException (
+					"Error reading page header");
+			
+			version = data [4];
+			this.flags = (PageFlags) data [5];
+			absolute_granular_position = data.Mid(6, 8).ToULong (
+				false);
+			stream_serial_number = data.Mid(14, 4).ToUInt (false);
+			page_sequence_number = 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 coresponding data for this
+			// count.
+			int page_segment_count = data [26];
+			ByteVector page_segments =
+				file.ReadBlock (page_segment_count);
+			
+			// Another sanity check.
+			if (page_segment_count < 1 ||
+				page_segments.Count != page_segment_count)
+				throw new CorruptFileException (
+					"Incorrect number of page segments");
+			
+			// The base size of an Ogg page 27 bytes plus the number
+			// of lacing values.
+			size = (uint)(27 + page_segment_count);
+			packet_sizes = new List<int> ();
+			
+			int packet_size = 0;
+			data_size = 0;
+			
+			for (int i = 0; i < page_segment_count; i++) {
+				data_size += page_segments [i];
+				packet_size += page_segments [i];
+					
+				if (page_segments [i] < 255) {
+					packet_sizes.Add (packet_size);
+					packet_size = 0;
+				}
+			}
+			
+			if (packet_size > 0)
+				packet_sizes.Add (packet_size);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="PageHeader" /> by copying the values from another
+		///    instance, offsetting the page number and applying new
+		///    flags.
+		/// </summary>
+		/// <param name="original">
+		///    A <see cref="PageHeader"/> object to copy the values
+		///    from.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="uint"/> value specifying how much to offset
+		///    the page sequence number in the new instance.
+		/// </param>
+		/// <param name="flags">
+		///    A <see cref="PageFlags"/> value specifying the flags to
+		///    use in the new instance.
+		/// </param>
+		public PageHeader (PageHeader original, uint offset,
+		                   PageFlags flags)
+		{
+			version = original.version;
+			this.flags = flags;
+			absolute_granular_position =
+				original.absolute_granular_position;
+			stream_serial_number = original.stream_serial_number;
+			page_sequence_number =
+				original.page_sequence_number + offset;
+			size = original.size;
+			data_size = original.data_size;
+			packet_sizes = new List<int> ();
+			
+			if (page_sequence_number == 0 &&
+				(flags & PageFlags.FirstPacketContinued) == 0)
+				this.flags |= PageFlags.FirstPageOfStream;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the sizes for the packets in the page
+		///    described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int[]" /> containing the packet sizes.
+		/// </value>
+		public int [] PacketSizes {
+			get {return packet_sizes.ToArray ();}
+			set {
+				packet_sizes.Clear ();
+				packet_sizes.AddRange (value);
+			}
+		}
+
+		/// <summary>
+		///    Gets the flags for the page described by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="PageFlags" /> value containing the page
+		///    flags.
+		/// </value>
+		public PageFlags Flags {
+			get {return flags;}
+		}
+		
+		/// <summary>
+		///    Gets the absolute granular position of the page described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="long" /> value containing the absolute
+		///    granular position of the page.
+		/// </value>
+		public long AbsoluteGranularPosition {
+			get {return (long) absolute_granular_position;}
+		}
+		
+		/// <summary>
+		///    Gets the sequence number of the page described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the sequence
+		///    number of the page.
+		/// </value>
+		public uint PageSequenceNumber {
+			get {return page_sequence_number;}
+		}
+		
+		/// <summary>
+		///    Gets the serial number of stream that the page described
+		///    by the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the stream serial
+		///    number.
+		/// </value>
+		public uint StreamSerialNumber {
+			get {return stream_serial_number;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the header as it appeared on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the header size.
+		/// </value>
+		public uint Size {
+			get {return size;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the data portion of the page described
+		///    by the current instance as it appeared on disk.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the data size.
+		/// </value>
+		public uint DataSize {
+			get {return data_size;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw Ogg page header.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			
+			data.Add ("OggS");
+			data.Add (version); // stream structure version
+			data.Add ((byte) flags);
+			data.Add (ByteVector.FromULong (
+				absolute_granular_position, false));
+			data.Add (ByteVector.FromUInt (
+				stream_serial_number, false));
+			data.Add (ByteVector.FromUInt (
+				(uint) page_sequence_number, false));
+			data.Add (new ByteVector (4, 0)); // checksum, to be filled in later.
+			ByteVector page_segments = LacingValues;
+			data.Add ((byte) page_segments.Count);
+			data.Add (page_segments);
+			
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region Private Properties
+		
+		/// <summary>
+		///    Gets the rendered lacing values for the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered lacing values.
+		/// </value>
+		private ByteVector LacingValues {
+			get {
+				ByteVector data = new ByteVector ();
+				
+				int [] sizes = PacketSizes;
+				
+				for (int i = 0; i < sizes.Length; i ++) {
+					// 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.
+					
+					int quot = sizes [i] / 255;
+					int rem  = sizes [i] % 255;
+					
+					for (int j = 0; j < quot; j++)
+						data.Add ((byte) 255);
+					
+					if (i < sizes.Length - 1 ||
+						(packet_sizes [i] % 255) != 0)
+						data.Add ((byte) rem);
+				}
+				
+				return data;
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region IEquatable
+		
+		/// <summary>
+		///    Generates a hash code for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value containing the hash code for
+		///    the current instance.
+		/// </returns>
+		public override int GetHashCode ()		{
+			unchecked {
+				return (int) (LacingValues.GetHashCode () ^
+					version ^ (int) flags ^
+					(int) absolute_granular_position ^
+					stream_serial_number ^
+					page_sequence_number ^ size ^
+					data_size);
+			}
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another object.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="object" /> to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public override bool Equals (object other)
+		{
+			if (!(other is PageHeader))
+				return false;
+			
+			return Equals ((PageHeader) other);
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another instance of <see cref="PageHeader" />.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="PageHeader" /> object to compare to the
+		///    current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public bool Equals (PageHeader other)
+		{
+			return packet_sizes == other.packet_sizes &&
+				version == other.version &&
+				flags == other.flags &&
+				absolute_granular_position ==
+					other.absolute_granular_position &&
+				stream_serial_number ==
+					other.stream_serial_number &&
+				page_sequence_number ==
+					other.page_sequence_number &&
+				size == other.size &&
+				data_size == other.data_size;
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="PageHeader" /> are equal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="PageHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="PageHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    equal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator == (PageHeader first,
+		                                PageHeader second)
+		{
+			return first.Equals (second);
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="PageHeader" /> differ.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="PageHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="PageHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    unequal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator != (PageHeader first,
+		                                PageHeader second)
+		{
+			return !first.Equals (second);
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/Paginator.cs b/lib/TagLib/TagLib/Ogg/Paginator.cs
new file mode 100644
index 0000000..3bac6d2
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/Paginator.cs
@@ -0,0 +1,275 @@
+//
+// Paginator.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   oggpage.cpp from TagLib
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// Copyright (C) 2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    This class accepts a sequence of pages for a single Ogg stream,
+	///    accepts changes, and produces a new sequence of pages to write to
+	///    disk.
+	/// </summary>
+	public class Paginator
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the packets to paginate.
+		/// </summary>
+		private ByteVectorCollection packets =
+			new ByteVectorCollection ();
+		
+		/// <summary>
+		///    Contains the first page header.
+		/// </summary>
+		private PageHeader? first_page_header = null;
+		
+		/// <summary>
+		///    Contains the codec to use.
+		/// </summary>
+		private Codec codec;
+		
+		/// <summary>
+		///    contains the number of pages read.
+		/// </summary>
+		private int pages_read = 0;
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Paginator" /> for a given <see cref="Codec" />
+		///    object.
+		/// </summary>
+		/// <param name="codec">
+		///    A <see cref="Codec"/> object to use when processing
+		///    packets.
+		/// </param>
+		public Paginator (Codec codec)
+		{
+			this.codec = codec;
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Adds the next page to the current instance.
+		/// </summary>
+		/// <param name="page">
+		///    The next <see cref="Page" /> object found in the stream.
+		/// </param>
+		public void AddPage (Page page)
+		{
+			pages_read ++;
+			
+			if (first_page_header == null)
+				first_page_header = page.Header;
+			
+			if (page.Packets.Length == 0)
+				return;
+			
+			ByteVector[] page_packets = page.Packets;
+			
+			for (int i = 0; i < page_packets.Length; i ++) {
+				if ((page.Header.Flags & PageFlags
+					.FirstPacketContinued) != 0 && i == 0 &&
+					packets.Count > 0)
+					packets [packets.Count - 1].Add (page_packets [0]);
+				else
+					packets.Add (page_packets [i]);
+			}
+		}
+		
+		/// <summary>
+		///    Stores a Xiph comment in the codec-specific comment
+		///    packet.
+		/// </summary>
+		/// <param name="comment">
+		///    A <see cref="XiphComment" /> object to store in the
+		///    comment packet.
+		/// </param>
+		public void SetComment (XiphComment comment)
+		{
+			codec.SetCommentPacket (packets, comment);
+		}
+		
+		/// <summary>
+		///    Repaginates the pages passed into the current instance to
+		///    handle changes made to the Xiph comment.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="Page[]" /> containing the new page
+		///    collection.
+		/// </returns>
+		[Obsolete("Use Paginator.Paginate(out int)")]
+		public Page [] Paginate ()
+		{
+			int dummy;
+			return Paginate (out dummy);
+		}
+		
+		/// <summary>
+		///    Repaginates the pages passed into the current instance to
+		///    handle changes made to the Xiph comment.
+		/// </summary>
+		/// <param name="change">
+		///    A <see cref="int" /> value reference containing the
+		///    the difference between the number of pages returned and
+		///    the number of pages that were added to the class.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Page[]" /> containing the new page
+		///    collection.
+		/// </returns>
+		public Page [] Paginate (out int change)
+		{
+			// Ogg Pagination: Welcome to sucksville!
+			// If you don't understand this, you're not alone.
+			// It is confusing as Hell.
+			
+			// TODO: Document this method, in the mean time, there
+			// is always http://xiph.org/ogg/doc/framing.html
+			
+			if (pages_read == 0) {
+				change = 0;
+				return new Page [0];
+			}
+			
+			int count = pages_read;
+			ByteVectorCollection packets = new ByteVectorCollection (
+				this.packets);
+			PageHeader first_header = (PageHeader) first_page_header;
+			List<Page> pages = new List<Page> ();
+			uint index = 0;
+			bool bos = first_header.PageSequenceNumber == 0;
+			
+			if (bos) {
+				pages.Add (new Page (new ByteVectorCollection (packets [0]), first_header));
+				index ++;
+				packets.RemoveAt (0);
+				count --;
+			}
+			
+			int lacing_per_page = 0xfc;
+			if (count > 0) {
+				int total_lacing_bytes = 0;
+				
+				for (int i = 0; i < packets.Count; i ++)
+					total_lacing_bytes += GetLacingValueLength (
+						packets, i);
+				
+				lacing_per_page = Math.Min (total_lacing_bytes / count + 1, lacing_per_page);
+			}
+			
+			int lacing_bytes_used = 0;
+			ByteVectorCollection page_packets = new ByteVectorCollection ();
+			bool first_packet_continued = false;
+			
+			while (packets.Count > 0) {
+				int packet_bytes = GetLacingValueLength (packets, 0);
+				int remaining = lacing_per_page - lacing_bytes_used;
+				bool whole_packet = packet_bytes <= remaining;
+				if (whole_packet) {
+					page_packets.Add (packets [0]);
+					lacing_bytes_used += packet_bytes;
+					packets.RemoveAt (0);
+				} else {
+					page_packets.Add (packets [0].Mid (0, remaining * 0xff));
+					packets [0] = packets [0].Mid (remaining * 0xff);
+					lacing_bytes_used += remaining;
+				}
+				
+				if (lacing_bytes_used == lacing_per_page) {
+					pages.Add (new Page (page_packets,
+						new PageHeader (first_header,
+							index, first_packet_continued ?
+							PageFlags.FirstPacketContinued :
+							PageFlags.None)));
+					page_packets = new ByteVectorCollection ();
+					lacing_bytes_used = 0;
+					index ++;
+					count --;
+					first_packet_continued = !whole_packet;
+				}
+			}
+			
+			if (page_packets.Count > 0) {
+				pages.Add (new Page (page_packets,
+					new PageHeader (
+						first_header.StreamSerialNumber,
+						index, first_packet_continued ?
+						PageFlags.FirstPacketContinued :
+						PageFlags.None)));
+				index ++;
+				count --;
+			}
+			change = -count;
+			return pages.ToArray ();
+		}
+		
+#endregion
+		
+		
+		
+#region Private Methods
+		
+		/// <summary>
+		///    Gets the number of lacing value bytes that would be
+		///    required for a given packet.
+		/// </summary>
+		/// <param name="packets">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    the packet.
+		/// </param>
+		/// <param name="index">
+		///    A <see cref="int" /> value containing the index of the
+		///    packet to compute.
+		/// </param>
+		/// <returns>
+		///    A <see cref="int" /> value containing the number of bytes
+		///    needed to store the length.
+		/// </returns>
+		private static int GetLacingValueLength (ByteVectorCollection packets,
+		                                         int index)
+		{
+			int size = packets [index].Count;
+			return size / 0xff + ((index + 1 < packets.Count ||
+				size % 0xff > 0) ? 1 : 0);
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Ogg/XiphComment.cs b/lib/TagLib/TagLib/Ogg/XiphComment.cs
new file mode 100644
index 0000000..d6259d4
--- /dev/null
+++ b/lib/TagLib/TagLib/Ogg/XiphComment.cs
@@ -0,0 +1,1204 @@
+//
+// XiphComment.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   xiphcomment.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace TagLib.Ogg
+{
+	/// <summary>
+	///    This class extends <see cref="TagLib.Tag" /> and implements <see
+	///    cref="T:System.Collections.Generic.IEnumerable`1" /> to provide
+	///    support for reading and writing Xiph comments.
+	/// </summary>
+	public class XiphComment : TagLib.Tag, IEnumerable<string>
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the comment fields.
+		/// </summary>
+		private Dictionary<string,string[]> field_list =
+			new Dictionary<string,string[]> ();
+		
+		/// <summary>
+		///    Contains the ventor ID.
+		/// </summary>
+		private string vendor_id;
+		
+		/// <summary>
+		///    Contains the field identifier to use for <see
+		///    cref="Comment" />.
+		/// </summary>
+		private string comment_field = "DESCRIPTION";
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="XiphComment" /> with no contents.
+		/// </summary>
+		public XiphComment ()
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="XiphComment" /> by reading the contents of a raw
+		///    Xiph Comment from a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing a raw Xiph
+		///    comment.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public XiphComment (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+
+			Parse (data);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Gets the field data for a given field identifier.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string"/> object containing the field
+		///    identifier.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]"/> containing the field data or an
+		///    empty array if the field was not found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public string [] GetField (string key)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			key = key.ToUpper (CultureInfo.InvariantCulture);
+			
+			if (!field_list.ContainsKey (key))
+				return new string [0];
+			
+			return (string []) field_list [key].Clone ();
+		}
+		
+		/// <summary>
+		///    Gets the first field for a given field identifier.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string"/> object containing the field
+		///    identifier.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string"/> containing the field data or <see
+		///    langword="null" /> if the field was not found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public string GetFirstField (string key)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			string [] values = GetField (key);
+			return (values.Length > 0) ? values [0] : null;
+		}
+		
+		/// <summary>
+		///    Sets the contents of a specified field to a number.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string"/> object containing the field
+		///    identifier.
+		/// </param>
+		/// <param name="number">
+		///    A <see cref="uint" /> value to set the field to.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void SetField (string key, uint number)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			if (number == 0)
+				RemoveField (key);
+			else
+				SetField (key, number.ToString (
+					CultureInfo.InvariantCulture));
+		}
+		
+		/// <summary>
+		///    Sets the contents of a specified field to the contents of
+		///    a <see cref="string[]" />.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string"/> object containing the field
+		///    identifier.
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="string[]"/> containing the values to store
+		///    in the current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void SetField (string key, params string [] values)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			key = key.ToUpper (CultureInfo.InvariantCulture);
+			
+			if (values == null || values.Length == 0) {
+				RemoveField (key);
+				return;
+			}
+			
+			List <string> result = new List<string> ();
+			foreach (string text in values)
+				if (text != null && text.Trim ().Length != 0)
+					result.Add (text);
+			
+			if (result.Count == 0)
+				RemoveField (key);
+			else if (field_list.ContainsKey (key))
+				field_list [key] = result.ToArray ();
+			else
+				field_list.Add (key, result.ToArray ());
+		}
+		
+		/// <summary>
+		///    Removes a field and all its values from the current
+		///    instance.
+		/// </summary>
+		/// <param name="key">
+		///    A <see cref="string"/> object containing the field
+		///    identifier.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="key" /> is <see langword="null" />.
+		/// </exception>
+		public void RemoveField (string key)
+		{
+			if (key == null)
+				throw new ArgumentNullException ("key");
+			
+			key = key.ToUpper (CultureInfo.InvariantCulture);
+			
+			field_list.Remove (key);
+		}
+		
+		/// <summary>
+		///    Renders the current instance as a raw Xiph comment,
+		///    optionally adding a framing bit.
+		/// </summary>
+		/// <param name="addFramingBit">
+		///    If <see langword="true" />, a framing bit will be added to
+		///    the end of the content.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		public ByteVector Render (bool addFramingBit)
+		{
+			ByteVector data = new ByteVector ();
+
+			// Add the vendor ID length and the vendor ID.  It's
+			// important to use the length of the data(String::UTF8)
+			// rather than the lenght of the the string since this
+			// is UTF8 text and there may be more characters in the
+			// data than in the UTF16 string.
+			
+			ByteVector vendor_data = ByteVector.FromString (
+				vendor_id, StringType.UTF8);
+
+			data.Add (ByteVector.FromUInt ((uint) vendor_data.Count,
+				false));
+			data.Add (vendor_data);
+
+			// Add the number of fields.
+
+			data.Add (ByteVector.FromUInt (FieldCount, false));
+
+			foreach (KeyValuePair<string,string[]> entry in field_list) {
+				// And now iterate over the values of the
+				// current list.
+
+				foreach (string value in entry.Value) {
+					ByteVector field_data =
+						ByteVector.FromString (
+							entry.Key, StringType.UTF8);
+					field_data.Add ((byte) '=');
+					field_data.Add (ByteVector.FromString (
+						value, StringType.UTF8));
+					
+					data.Add (ByteVector.FromUInt ((uint)
+						field_data.Count, false));
+					data.Add (field_data);
+				}
+			}
+
+			// Append the "framing bit".
+			if (addFramingBit)
+				data.Add ((byte) 1);
+
+			return data;
+		}
+
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the number of fields contained in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    fields in the current instance.
+		/// </value>
+		public uint FieldCount {
+			get {
+				uint count = 0;
+				foreach (string [] values in field_list.Values)
+					count += (uint) values.Length;
+				
+				return count;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the vendor ID for the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the vendor ID
+		///    for current instance.
+		/// </value>
+		public string VendorId {
+			get {return vendor_id;}
+		}
+		
+#endregion
+		
+		
+		
+#region Protected Methods
+		
+		/// <summary>
+		///    Populates and initializes a new instance of <see
+		///    cref="XiphComment" /> by reading the contents of a raw
+		///    Xiph Comment from a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing a raw Xiph
+		///    comment.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		protected void Parse (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("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 vendor_length = (int) data.Mid (pos, 4)
+				.ToUInt (false);
+			pos += 4;
+
+			vendor_id = data.ToString (StringType.UTF8, pos,
+				vendor_length);
+			pos += vendor_length;
+
+			// Next the number of fields in the comment vector.
+
+			int comment_fields = (int) data.Mid (pos, 4)
+				.ToUInt (false);
+			pos += 4;
+
+			for(int i = 0; i < comment_fields; 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 comment_length = (int) data.Mid (pos, 4)
+					.ToUInt (false);
+				pos += 4;
+
+				string comment = data.ToString (StringType.UTF8,
+					pos, comment_length);
+				pos += comment_length;
+
+				int comment_separator_position = comment
+					.IndexOf ('=');
+
+				if (comment_separator_position < 0)
+					continue;
+
+				string key = comment.Substring (0,
+					comment_separator_position)
+					.ToUpper (
+						CultureInfo.InvariantCulture);
+				string value = comment.Substring (
+					comment_separator_position + 1);
+				string [] values;
+				
+				if (field_list.TryGetValue (key, out values)) {
+					Array.Resize <string> (ref values,
+						values.Length + 1);
+					values [values.Length - 1] = value;
+					field_list [key] = values;
+				} else {
+					SetField (key, value);
+				}
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region IEnumerable
+		
+		/// <summary>
+		///    Gets an enumerator for enumerating through the the field
+		///    identifiers.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="T:System.Collections.IEnumerator`1" /> for
+		///    enumerating through the field identifiers.
+		/// </returns>
+		public IEnumerator<string> GetEnumerator ()
+		{
+			return field_list.Keys.GetEnumerator();
+		}
+		
+		IEnumerator IEnumerable.GetEnumerator()
+		{
+			return field_list.Keys.GetEnumerator();
+		}
+		
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.Xiph" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.Xiph;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TITLE" field.
+		/// </remarks>
+		public override string Title {
+			get {return GetFirstField ("TITLE");}
+			set {SetField ("TITLE", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the Track Title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort name of 
+		///    the Track Title of the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TITLESORT"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string TitleSort {
+			get {return GetFirstField ("TITLESORT");}
+			set {SetField ("TITLESORT", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ARTIST" field.
+		/// </remarks>
+		public override string [] Performers {
+			get {return GetField ("ARTIST");}
+			set {SetField ("ARTIST", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ARTISTSORT" field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string [] PerformersSort {
+			get {return GetField ("ARTISTSORT");}
+			set {SetField ("ARTISTSORT", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ALBUMARTIST"
+		///    field.
+		/// </remarks>
+		public override string [] AlbumArtists {
+			get {
+				// First try to get AlbumArtist, if that comment is not present try: 
+				// ENSEMBLE: set by TAG & RENAME
+				// ALBUM ARTIST: set by The GodFather
+				string[] value = GetField("ALBUMARTIST");
+				if (value != null && value.Length > 0)
+				  return value;
+
+				value = GetField("ALBUM ARTIST");
+				if (value != null && value.Length > 0)
+				  return value;
+
+				return GetField ("ENSEMBLE"); 
+			}
+			set {SetField ("ALBUMARTIST", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the band or artist who
+		///    is credited in the creation of the entire album or
+		///    collection containing the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the band or artist who is credited in the creation
+		///    of the entire album or collection containing the media
+		///    described by the current instance or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ALBUMARTISTSORT"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string [] AlbumArtistsSort {
+			get {return GetField ("ALBUMARTISTSORT");}
+			set {SetField ("ALBUMARTISTSORT", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "COMPOSER" field.
+		/// </remarks>
+		public override string [] Composers {
+			get {return GetField ("COMPOSER");}
+			set {SetField ("COMPOSER", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the composers of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the composer of the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "COMPOSERSORT"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string [] ComposersSort {
+			get {return GetField ("COMPOSERSORT");}
+			set {SetField ("COMPOSERSORT", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ALBUM" field.
+		/// </remarks>
+		public override string Album {
+			get {return GetFirstField ("ALBUM");}
+			set {SetField ("ALBUM", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the Album Title of
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the sort name of 
+		///    the Album Title of the media described by the current
+		///    instance or null if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ALBUMSORT"
+		///    field.
+		///    http://musicbrainz.org/doc/PicardTagMapping
+		/// </remarks>
+		public override string AlbumSort {
+			get {return GetFirstField ("ALBUMSORT");}
+			set {SetField ("ALBUMSORT", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "COMMENT" or
+		///    "DESCRIPTION" field, preferring "DESCRIPTION" but using
+		///    "COMMENT" if that is the field used by the comment.
+		/// </remarks>
+		public override string Comment {
+			get {
+				string value = GetFirstField (comment_field);
+				if (value != null || comment_field == "COMMENT")
+					return value;
+				
+				comment_field = "COMMENT";
+				return GetFirstField (comment_field);
+			}
+			set {SetField (comment_field, value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "GENRE" field.
+		/// </remarks>
+		public override string [] Genres {
+			get {return GetField ("GENRE");}
+			set {SetField ("GENRE", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "DATE" field. If a
+		///    value greater than 9999 is set, this property will be
+		///    cleared.
+		/// </remarks>
+		public override uint Year {
+			get {
+				string text = GetFirstField ("DATE");
+				uint value;
+				return (text != null && uint.TryParse (
+					text.Length > 4 ? text.Substring (0, 4)
+					: text, out value)) ? value : 0;
+			}
+			set {SetField ("DATE", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TRACKNUMER"
+		///    field.
+		/// </remarks>
+		public override uint Track {
+			get {
+				string text = GetFirstField ("TRACKNUMBER");
+				string [] values;
+				uint value;
+				
+				if (text != null && (values = text.Split ('/'))
+					.Length > 0 && uint.TryParse (
+						values [0], out value))
+					return value;
+				
+				return 0;
+			}
+			set {
+				SetField ("TRACKTOTAL", TrackCount);
+				SetField ("TRACKNUMBER", value);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TRACKTOTAL" field
+		///    but is capable of reading from "TRACKNUMBER" if the total
+		///    is stored in {track}/{count} format.
+		/// </remarks>
+		public override uint TrackCount {
+			get {
+				string text;
+				string [] values;
+				uint value;
+				
+				if ((text = GetFirstField ("TRACKTOTAL")) !=
+					null && uint.TryParse (text, out value))
+					return value;
+				
+				if ((text = GetFirstField ("TRACKNUMBER")) !=
+					null && (values = text.Split ('/'))
+					.Length > 1 && uint.TryParse (
+						values [1], out value))
+					return value;
+				
+				return 0;
+			}
+			set {SetField ("TRACKTOTAL", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "DISCNUMBER"
+		///    field.
+		/// </remarks>
+		public override uint Disc {
+			get {
+				string text = GetFirstField ("DISCNUMBER");
+				string [] values;
+				uint value;
+				
+				if (text != null && (values = text.Split ('/'))
+					.Length > 0 && uint.TryParse (
+						values [0], out value))
+					return value;
+				
+				return 0;
+			}
+			set {
+				SetField ("DISCTOTAL", DiscCount);
+				SetField ("DISCNUMBER", value);
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "DISCTOTAL" field
+		///    but is capable of reading from "DISCNUMBER" if the total
+		///    is stored in {disc}/{count} format.
+		/// </remarks>
+		public override uint DiscCount {
+			get {
+				string text;
+				string [] values;
+				uint value;
+
+				if ((text = GetFirstField ("DISCTOTAL")) != null
+					&& uint.TryParse (text, out value))
+					return value;
+				
+				if ((text = GetFirstField ("DISCNUMBER")) !=
+					null && (values = text.Split ('/'))
+					.Length > 1 && uint.TryParse (
+						values [1], out value))
+					return value;
+				
+				return 0;
+			}
+			set {SetField ("DISCTOTAL", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "LYRICS" field.
+		/// </remarks>
+		public override string Lyrics {
+			get {return GetFirstField ("LYRICS");}
+			set {SetField ("LYRICS", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "GROUPING" field.
+		/// </remarks>
+		public override string Grouping {
+			get {return GetFirstField ("GROUPING");}
+			set {SetField ("GROUPING", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TEMPO" field.
+		/// </remarks>
+		public override uint BeatsPerMinute {
+			get {
+				string text = GetFirstField ("TEMPO");
+				double value;
+				return (text != null &&
+					double.TryParse (text, out value) &&
+					value > 0) ? (uint) Math.Round (value) :
+					0;
+			}
+			set {SetField ("TEMPO", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "CONDUCTOR" field.
+		/// </remarks>
+		public override string Conductor {
+			get {return GetFirstField ("CONDUCTOR");}
+			set {SetField ("CONDUCTOR", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "COPYRIGHT" field.
+		/// </remarks>
+		public override string Copyright {
+			get {return GetFirstField ("COPYRIGHT");}
+			set {SetField ("COPYRIGHT", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the MusicBrainz Artist ID for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ArtistID for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ARTISTID" field.
+		/// </remarks>
+		public override string MusicBrainzArtistId {
+			get {return GetFirstField ("MUSICBRAINZ_ARTISTID");}
+			set {SetField ("MUSICBRAINZ_ARTISTID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release ID for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseID for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMID" field.
+		/// </remarks>
+		public override string MusicBrainzReleaseId {
+			get {return GetFirstField ("MUSICBRAINZ_ALBUMID");}
+			set {SetField ("MUSICBRAINZ_ALBUMID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Artist ID for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseArtistID for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMARTISTID" field.
+		/// </remarks>
+		public override string MusicBrainzReleaseArtistId {
+			get {return GetFirstField ("MUSICBRAINZ_ALBUMARTISTID");}
+			set {SetField ("MUSICBRAINZ_ALBUMARTISTID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Track ID for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    TrackID for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_TRACKID" field.
+		/// </remarks>
+		public override string MusicBrainzTrackId {
+			get {return GetFirstField ("MUSICBRAINZ_TRACKID");}
+			set {SetField ("MUSICBRAINZ_TRACKID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Disc ID for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    DiscID for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_DISCID" field.
+		/// </remarks>
+		public override string MusicBrainzDiscId {
+			get {return GetFirstField ("MUSICBRAINZ_DISCID");}
+			set {SetField ("MUSICBRAINZ_DISCID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicIP PUID
+		///    for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICIP_PUID" field.
+		/// </remarks>
+		public override string MusicIpId {
+			get {return GetFirstField ("MUSICIP_PUID");}
+			set {SetField ("MUSICIP_PUID", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the Amazon ID for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the AmazonID
+		///    for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ASIN" field.
+		/// </remarks>
+		public override string AmazonId {
+			get {return GetFirstField ("ASIN");}
+			set {SetField ("ASIN", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Status for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseStatus for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMSTATUS" field.
+		/// </remarks>
+		public override string MusicBrainzReleaseStatus {
+			get {return GetFirstField ("MUSICBRAINZ_ALBUMSTATUS");}
+			set {SetField ("MUSICBRAINZ_ALBUMSTATUS", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Type for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseType for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "MUSICBRAINZ_ALBUMTYPE" field.
+		/// </remarks>
+		public override string MusicBrainzReleaseType {
+			get {return GetFirstField ("MUSICBRAINZ_ALBUMTYPE");}
+			set {SetField ("MUSICBRAINZ_ALBUMTYPE", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Country for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the MusicBrainz
+		///    ReleaseCountry for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "RELEASECOUNTRY" field.
+		/// </remarks>
+		public override string MusicBrainzReleaseCountry {
+			get {return GetFirstField ("RELEASECOUNTRY");}
+			set {SetField ("RELEASECOUNTRY", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    <para>This property is implemented using the COVERART
+		///    field.</para>
+		/// </remarks>
+		public override IPicture [] Pictures {
+			get {
+				string[] covers = GetField ("COVERART");
+				IPicture[] pictures = new Picture[covers.Length];
+				for (int ii = 0; ii < covers.Length; ii++) {
+					ByteVector data = new ByteVector (Convert.FromBase64String (covers[ii]));
+					pictures[ii] = new Picture (data);
+				}
+				return pictures;
+			}
+			set {
+				string[] covers = new string[value.Length];
+				for (int ii = 0; ii < value.Length; ii++) {
+					IPicture old = value[ii];
+					covers[ii] = Convert.ToBase64String (old.Data.Data);
+				}
+				SetField ("COVERART", covers);
+			}
+		}
+
+		/// <summary>
+		///    Gets and sets whether or not the album described by the
+		///    current instance is a compilation.
+		/// </summary>
+		/// <value>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    album described by the current instance is a compilation.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "COMPILATION" field.
+		/// </remarks>
+		public bool IsCompilation {
+			get {
+				string text;
+				int value;
+
+				if ((text = GetFirstField ("COMPILATION")) !=
+					null && int.TryParse (text, out value)) {
+						return value == 1;
+				}
+				return false;
+			}
+			set {
+				if (value) {
+					SetField ("COMPILATION", "1");
+				} else {
+					RemoveField ("COMPILATION");
+				}
+			}
+		}
+
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance does not
+		///    any values. Otherwise <see langword="false" />.
+		/// </value>
+		public override bool IsEmpty {
+			get {
+				foreach (string [] values in field_list.Values)
+					if (values.Length != 0)
+						return false;
+				
+				return true;
+			}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			field_list.Clear ();
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Picture.cs b/lib/TagLib/TagLib/Picture.cs
new file mode 100644
index 0000000..8d63f4f
--- /dev/null
+++ b/lib/TagLib/TagLib/Picture.cs
@@ -0,0 +1,450 @@
+//
+// Picture.cs: Provides IPicture and Picture.
+//
+// Author:
+//   Aaron Bockover (abockover novell com)
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   attachedpictureframe.cpp from TagLib
+//
+// Copyright (C) 2006 Novell, Inc.
+// Copyright (C) 2007 Brian Nickel
+// Copyright (C) 2004 Scott Wheeler (Original 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
+//
+
+using System;
+
+namespace TagLib {
+	/// <summary>
+	///    Specifies the type of content appearing in the picture.
+	/// </summary>
+	public enum PictureType
+	{
+		/// <summary>
+		///    The picture is of a type other than those specified.
+		/// </summary>
+		Other = 0x00,
+		
+		/// <summary>
+		///    The picture is a 32x32 PNG image that should be used when
+		///    displaying the file in a browser.
+		/// </summary>
+		FileIcon = 0x01,
+		
+		/// <summary>
+		///    The picture is of an icon different from <see
+		///    cref="FileIcon" />.
+		/// </summary>
+		OtherFileIcon = 0x02,
+		
+		/// <summary>
+		///    The picture is of the front cover of the album.
+		/// </summary>
+		FrontCover = 0x03,
+		
+		/// <summary>
+		///    The picture is of the back cover of the album.
+		/// </summary>
+		BackCover = 0x04,
+		
+		/// <summary>
+		///    The picture is of a leaflet page including with the
+		///    album.
+		/// </summary>
+		LeafletPage = 0x05,
+		
+		/// <summary>
+		///    The picture is of the album or disc itself.
+		/// </summary>
+		Media = 0x06,
+		// Image from the album itself
+		
+		/// <summary>
+		///    The picture is of the lead artist or soloist.
+		/// </summary>
+		LeadArtist = 0x07,
+		
+		/// <summary>
+		///    The picture is of the artist or performer.
+		/// </summary>
+		Artist = 0x08,
+		
+		/// <summary>
+		///    The picture is of the conductor.
+		/// </summary>
+		Conductor = 0x09,
+		
+		/// <summary>
+		///    The picture is of the band or orchestra.
+		/// </summary>
+		Band = 0x0A,
+		
+		/// <summary>
+		///    The picture is of the composer.
+		/// </summary>
+		Composer = 0x0B,
+		
+		/// <summary>
+		///    The picture is of the lyricist or text writer.
+		/// </summary>
+		Lyricist = 0x0C,
+		
+		/// <summary>
+		///    The picture is of the recording location or studio.
+		/// </summary>
+		RecordingLocation = 0x0D,
+		
+		/// <summary>
+		///    The picture is one taken during the track's recording.
+		/// </summary>
+		DuringRecording = 0x0E,
+		
+		/// <summary>
+		///    The picture is one taken during the track's performance.
+		/// </summary>
+		DuringPerformance = 0x0F,
+		
+		/// <summary>
+		///    The picture is a capture from a movie screen.
+		/// </summary>
+		MovieScreenCapture = 0x10,
+		
+		/// <summary>
+		///    The picture is of a large, colored fish.
+		/// </summary>
+		ColoredFish = 0x11,
+		
+		/// <summary>
+		///    The picture is an illustration related to the track.
+		/// </summary>
+		Illustration = 0x12,
+		
+		/// <summary>
+		///    The picture contains the logo of the band or performer.
+		/// </summary>
+		BandLogo = 0x13,
+		
+		/// <summary>
+		///    The picture is the logo of the publisher or record
+		///    company.
+		/// </summary>
+		PublisherLogo = 0x14
+	}
+	
+	/// <summary>
+	///    This interface provides generic information about a picture,
+	///    including its contents, as used by various formats.
+	/// </summary>
+	public interface IPicture
+	{
+		/// <summary>
+		///    Gets and sets the mime-type of the picture data
+		///    stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the mime-type
+		///    of the picture data stored in the current instance.
+		/// </value>
+		string MimeType {get; set;}
+		
+		/// <summary>
+		///    Gets and sets the type of content visible in the picture
+		///    stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="PictureType" /> containing the type of
+		///    content visible in the picture stored in the current
+		///    instance.
+		/// </value>
+		PictureType Type {get; set;}
+		
+		/// <summary>
+		///    Gets and sets a description of the picture stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the picture stored in the current instance.
+		/// </value>
+		string Description {get; set;}
+		
+		/// <summary>
+		///    Gets and sets the picture data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the picture
+		///    data stored in the current instance.
+		/// </value>
+		ByteVector Data {get; set;}
+	}
+	
+	/// <summary>
+	///    This class implements <see cref="IPicture" /> and provides
+	///    mechanisms for loading pictures from files.
+	/// </summary>
+	public class Picture : IPicture
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the mime-type.
+		/// </summary>
+		private string mime_type;
+		
+		/// <summary>
+		///    Contains the content type.
+		/// </summary>
+		private PictureType type;
+		
+		/// <summary>
+		///    Contains the description.
+		/// </summary>
+		private string description;
+		
+		/// <summary>
+		///    Contains the picture data.
+		/// </summary>
+		private ByteVector data;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Picture" /> with no data or values.
+		/// </summary>
+		public Picture ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Picture" /> by reading in the contents of a
+		///    specified file.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string"/> object containing the path of the
+		///    file to read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public Picture (string path)
+		{
+			if (path == null)
+				throw new ArgumentNullException ("path");
+			
+			Data = ByteVector.FromPath (path);
+			FillInMimeFromData ();
+			Description = path;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Picture" /> by reading in the contents of a
+		///    specified file abstraction.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="File.IFileAbstraction"/> object containing
+		///    abstraction of the file to read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public Picture (File.IFileAbstraction abstraction)
+		{
+			if (abstraction == null)
+				throw new ArgumentNullException ("abstraction");
+			
+			Data = ByteVector.FromFile (abstraction);
+			FillInMimeFromData ();
+			Description = abstraction.Name;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Picture" /> by using the contents of a <see
+		///    cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> object containing picture data
+		///    to use.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		public Picture (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			Data = new ByteVector (data);
+			FillInMimeFromData ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Static Methods
+		
+		/// <summary>
+		///    Creates a new <see cref="Picture" />, populating it with
+		///    the contents of a file.
+		/// </summary>
+		/// <param name="filename">
+		///    A <see cref="string" /> object containing the path to a
+		///    file to read the picture from.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="Picture" /> object containing the
+		///    contents of the file and with a mime-type guessed from
+		///    the file's contents.
+		/// </returns>
+		[Obsolete("Use Picture(string filename) constructor instead.")]
+		public static Picture CreateFromPath (string filename)
+		{
+			return new Picture (filename);
+		}
+		
+		/// <summary>
+		///    Creates a new <see cref="Picture" />, populating it with
+		///    the contents of a file.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="File.IFileAbstraction" /> object containing
+		///    the file abstraction to read the picture from.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="Picture" /> object containing the
+		///    contents of the file and with a mime-type guessed from
+		///    the file's contents.
+		/// </returns>
+		[Obsolete("Use Picture(File.IFileAbstraction abstraction) constructor instead.")]
+		public static Picture CreateFromFile (File.IFileAbstraction abstraction)
+		{
+			return new Picture (abstraction);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets and sets the mime-type of the picture data
+		///    stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the mime-type
+		///    of the picture data stored in the current instance.
+		/// </value>
+		public string MimeType {
+			get { return mime_type; }
+			set { mime_type = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the type of content visible in the picture
+		///    stored in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="PictureType" /> containing the type of
+		///    content visible in the picture stored in the current
+		///    instance.
+		/// </value>
+		public PictureType Type {
+			get { return type; }
+			set { type = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets a description of the picture stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the picture stored in the current instance.
+		/// </value>
+		public string Description {
+			get { return description; }
+			set { description = value; }
+		}
+		
+		/// <summary>
+		///    Gets and sets the picture data stored in the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ByteVector" /> object containing the picture
+		///    data stored in the current instance.
+		/// </value>
+		public ByteVector Data {
+			get { return data; }
+			set { data = value; }
+		}
+		
+		#endregion
+		
+		
+		
+		#region Private Methods
+		
+		/// <summary>
+		///    Fills in the mime type of the current instance by reading
+		///    the first few bytes of the file. If the format cannot be
+		///    identified, it assumed to be a JPEG file.
+		/// </summary>
+		private void FillInMimeFromData ()
+		{
+			string mimetype = "image/jpeg";
+			string ext = "jpg";
+			
+			if (Data.Count >= 4 &&
+				(Data[1] == 'P' &&
+				 Data[2] == 'N' &&
+				 Data[3] == 'G')) {
+				mimetype = "image/png";
+				ext = "png";
+			} else if (Data.Count >= 3 &&
+				(Data[0] == 'G' &&
+				 Data[1] == 'I' &&
+				 Data[2] == 'F')) {
+				mimetype = "image/gif";
+				ext = "gif";
+			} else if (Data.Count >= 2 &&
+				(Data[0] == 'B' &&
+				 Data[1] == 'M')) {
+				mimetype = "image/bmp";
+				ext = "bmp";
+			}
+			
+			MimeType = mimetype;
+			Type = PictureType.FrontCover;
+			Description = "cover." + ext;
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Properties.cs b/lib/TagLib/TagLib/Properties.cs
new file mode 100644
index 0000000..be3bc8b
--- /dev/null
+++ b/lib/TagLib/TagLib/Properties.cs
@@ -0,0 +1,484 @@
+//
+// Properties.cs: This class implements IAudioCodec and IVideoCodec
+// and combines codecs to create generic media properties for a file.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   audioproperties.cpp from TagLib
+//
+// Copyright (C) 2006,2007 Brian Nickel
+// Copyright (C) 2003 Scott Wheeler (Original 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
+//
+
+using System;
+using System.Text;
+using System.Collections.Generic;
+
+namespace TagLib {
+	/// <summary>
+	///    This class implements <see cref="IAudioCodec" />, <see
+	///    cref="IVideoCodec" /> and <see cref="IPhotoCodec" />
+	///    and combines codecs to create generic media properties
+	///    for a file.
+	/// </summary>
+	public class Properties : IAudioCodec, IVideoCodec, IPhotoCodec
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the codecs.
+		/// </summary>
+		private ICodec[] codecs = new ICodec [0];
+		
+		/// <summary>
+		///    Contains the duration.
+		/// </summary>
+		private TimeSpan duration = TimeSpan.Zero;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Properties" /> with no codecs or duration.
+		/// </summary>
+		/// <remarks>
+		///    <para>This constructor is used when media properties are
+		///    not read.</para>
+		/// </remarks>
+		public Properties ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Properties" /> with a specified duration and array
+		///    of codecs.
+		/// </summary>
+		/// <param name="duration">
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media, or <see cref="TimeSpan.Zero" /> if the duration is
+		///    to be read from the codecs.
+		/// </param>
+		/// <param name="codecs">
+		///    A <see cref="ICodec[]" /> containing the codecs to be
+		///    used in the new instance.
+		/// </param>
+		public Properties (TimeSpan duration, params ICodec[] codecs)
+		{
+			this.duration = duration;
+			if (codecs != null)
+				this.codecs = codecs;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="Properties" /> with a specified duration and
+		///    enumaration of codecs.
+		/// </summary>
+		/// <param name="duration">
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media, or <see cref="TimeSpan.Zero" /> if the duration is
+		///    to be read from the codecs.
+		/// </param>
+		/// <param name="codecs">
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object containing the
+		///    codec to be used in the new instance.
+		/// </param>
+		public Properties (TimeSpan duration, IEnumerable<ICodec> codecs)
+		{
+			this.duration = duration;
+			if (codecs != null)
+				this.codecs = new List<ICodec> (codecs)
+					.ToArray ();
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the codecs contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object containing the
+		///    <see cref="ICodec" /> objects contained in the current
+		///    instance.
+		/// </value>
+		public IEnumerable<ICodec> Codecs {
+			get {return codecs;}
+		}
+		
+		#endregion
+		
+		
+		
+		#region ICodec
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    If the duration was set in the constructor, that value is
+		///    returned. Otherwise, the longest codec duration is used.
+		/// </remarks>
+		public TimeSpan Duration {
+			get {
+				TimeSpan duration = this.duration;
+				
+				if (duration != TimeSpan.Zero)
+					return duration;
+				
+				foreach (ICodec codec in codecs)
+					if (codec != null &&
+						codec.Duration > duration)
+						duration = codec.Duration;
+				
+				return duration;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="MediaTypes" /> containing
+		///    the types of media represented by the current instance.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {
+				MediaTypes types = MediaTypes.None;
+				
+				foreach (ICodec codec in codecs)
+					if (codec != null)
+						types |= codec.MediaTypes;
+				
+				return types;
+			}
+		}
+		
+		/// <summary>
+		///    Gets a string description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    The value contains the descriptions of the codecs joined
+		///    by colons.
+		/// </remarks>
+		public string Description {
+			get {
+				StringBuilder builder = new StringBuilder ();
+				foreach (ICodec codec in codecs) {
+					if (codec == null)
+						continue;
+					
+					if (builder.Length != 0)
+						builder.Append ("; ");
+					
+					builder.Append (codec.Description);
+				}
+				return builder.ToString ();
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region IAudioCodec
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> containing the bitrate of the audio
+		///    represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    This value is equal to the first non-zero audio bitrate.
+		/// </remarks>
+		public int AudioBitrate {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Audio) == 0)
+						continue;
+					
+					IAudioCodec audio = codec as IAudioCodec;
+					
+					if (audio != null && audio.AudioBitrate != 0)
+						return audio.AudioBitrate;
+				}
+				
+				return 0;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> containing the sample rate of the
+		///    audio represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    This value is equal to the first non-zero audio sample
+		///    rate.
+		/// </remarks>
+		public int AudioSampleRate {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Audio) == 0)
+						continue;
+					
+					IAudioCodec audio = codec as IAudioCodec;
+					
+					if (audio != null && audio.AudioSampleRate != 0)
+						return audio.AudioSampleRate;
+				}
+				
+				return 0;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the number of bits per sample in the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of bits
+		///    per sample in the audio represented by the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    This value is equal to the first non-zero quantization.
+		/// </remarks>
+		public int BitsPerSample {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Audio) == 0)
+						continue;
+
+					ILosslessAudioCodec lossless = codec as ILosslessAudioCodec;
+
+					if (lossless != null && lossless.BitsPerSample != 0)
+						return lossless.BitsPerSample;
+				}
+
+				return 0;
+			}
+		}
+
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> object containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    This value is equal to the first non-zero audio channel
+		///    count.
+		/// </remarks>
+		public int AudioChannels {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Audio) == 0)
+						continue;
+					
+					IAudioCodec audio = codec as IAudioCodec;
+					
+					if (audio != null && audio.AudioChannels != 0)
+						return audio.AudioChannels;
+				}
+				
+				return 0;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region IVideoCodec
+		
+		/// <summary>
+		///    Gets the width of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> containing the width of the video
+		///    represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    This value is equal to the first non-zero video width.
+		/// </remarks>
+		public int VideoWidth {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Video) == 0)
+						continue;
+					
+					IVideoCodec video = codec as IVideoCodec;
+					
+					if (video != null && video.VideoWidth != 0)
+						return video.VideoWidth;
+				}
+				
+				return 0;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the height of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> containing the height of the video
+		///    represented by the current instance.
+		/// </value>
+		/// <remarks>
+		///    This value is equal to the first non-zero video height.
+		/// </remarks>
+		public int VideoHeight {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Video) == 0)
+						continue;
+					
+					IVideoCodec video = codec as IVideoCodec;
+					
+					if (video != null && video.VideoHeight != 0)
+						return video.VideoHeight;
+				}
+				
+				return 0;
+			}
+		}
+		
+		#endregion
+
+
+
+		#region IPhotoCodec
+
+		/// <summary>
+		///    Gets the width of the photo represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the width of the
+		///    photo represented by the current instance.
+		/// </value>
+		public int PhotoWidth {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Photo) == 0)
+						continue;
+					
+					IPhotoCodec photo = codec as IPhotoCodec;
+					
+					if (photo != null && photo.PhotoWidth != 0)
+						return photo.PhotoWidth;
+				}
+				
+				return 0;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the height of the photo represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the height of the
+		///    photo represented by the current instance.
+		/// </value>
+		public int PhotoHeight {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Photo) == 0)
+						continue;
+					
+					IPhotoCodec photo = codec as IPhotoCodec;
+					
+					if (photo != null && photo.PhotoHeight != 0)
+						return photo.PhotoHeight;
+				}
+				
+				return 0;
+			}
+		}
+
+		/// <summary>
+		///    Gets the (format specific) quality indicator of the photo
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value indicating the quality. A value
+		///    0 means that there was no quality indicator for the format
+		///    or the file.
+		/// </value>
+		public int PhotoQuality {
+			get {
+				foreach (ICodec codec in codecs) {
+					if (codec == null ||
+						(codec.MediaTypes & MediaTypes.Photo) == 0)
+						continue;
+					
+					IPhotoCodec photo = codec as IPhotoCodec;
+					
+					if (photo != null && photo.PhotoQuality != 0)
+						return photo.PhotoQuality;
+				}
+				
+				return 0;
+			}
+		}
+	
+
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/ReadOnlyByteVector.cs b/lib/TagLib/TagLib/ReadOnlyByteVector.cs
new file mode 100644
index 0000000..f28090b
--- /dev/null
+++ b/lib/TagLib/TagLib/ReadOnlyByteVector.cs
@@ -0,0 +1,216 @@
+//
+// ReadOnlyByteVector.cs: This class extends ByteVector" to provide an
+// immutable version.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace TagLib {
+	/// <summary>
+	///    This class extends <see cref="ByteVector" /> to provide an
+	///    immutable version.
+	/// </summary>
+	public sealed class ReadOnlyByteVector : ByteVector
+	{
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ReadOnlyByteVector" /> with no contents.
+		/// </summary>
+		public ReadOnlyByteVector () : base ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ReadOnlyByteVector" /> of a specified length filled
+		///    with bytes of a specified value.
+		/// </summary>
+		/// <param name="size">
+		///    A <see cref="int" /> specifying the number of bytes to
+		///    add to the new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="byte" /> specifying the value to use for the
+		///    bytes added to the new instance.
+		/// </param>
+		public ReadOnlyByteVector (int size, byte value)
+			: base (size, value)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ReadOnlyByteVector" /> of a specified length filled
+		///    with bytes with a value of zero.
+		/// </summary>
+		/// <param name="size">
+		///    A <see cref="int" /> specifying the number of bytes to
+		///    add to the new instance.
+		/// </param>
+		/// <remarks>
+		///    <para>To specify the value to fill the new instance with,
+		///    use <see cref="ReadOnlyByteVector(int,byte)" />.</para>
+		/// </remarks>
+		public ReadOnlyByteVector (int size) : this (size, 0)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ReadOnlyByteVector" /> by copying the contents from
+		///    another instance.
+		/// </summary>
+		/// <param name="vector">
+		///    A <see cref="ByteVector" /> object to copy the values
+		///    from.
+		/// </param>
+		public ReadOnlyByteVector (ByteVector vector) : base (vector)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ReadOnlyByteVector" /> by copying a specified
+		///    number of bytes from an array.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="byte[]" /> to copy values from.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> specifying the number of bytes to
+		///    copy.
+		/// </param>
+		/// <remarks>
+		///    <para>If copying the entire contents of an array, use
+		///    <see cref="ReadOnlyByteVector(byte[])" />.</para>
+		/// </remarks>
+		public ReadOnlyByteVector (byte [] data, int length)
+			: base (data, length)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ReadOnlyByteVector" /> by copying the contents of a
+		///    specified array.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="byte[]" /> to copy values from.
+		/// </param>
+		/// <remarks>
+		///    <para>To copy only part of the array, use <see
+		///    cref="ReadOnlyByteVector(byte[],int)" />.</para>
+		/// </remarks>
+		public ReadOnlyByteVector (params byte [] data) : base (data)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Operators
+		
+		/// <summary>
+		///    Implicitly converts a <see cref="byte" /> to a new
+		///    <see cref="ReadOnlyByteVector" />.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="byte" /> object to convert.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ReadOnlyByteVector" /> equivalent to
+		///    <paramref name="value" />.
+		/// </returns>
+		public static implicit operator ReadOnlyByteVector (byte value)
+		{
+			return new ReadOnlyByteVector (value);
+		}
+		
+		/// <summary>
+		///    Implicitly converts a <see cref="byte[]" /> to a new
+		///    <see cref="ReadOnlyByteVector" />.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="byte[]" /> object to convert.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ReadOnlyByteVector" /> equivalent to
+		///    <paramref name="value" />.
+		/// </returns>
+		public static implicit operator ReadOnlyByteVector (byte [] value)
+		{
+			return new ReadOnlyByteVector (value);
+		}
+		
+		/// <summary>
+		///    Implicitly converts a <see cref="string" /> object to a
+		///    new <see cref="ReadOnlyByteVector" /> using the UTF-8
+		///    encoding.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string" /> object to convert.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ReadOnlyByteVector" /> equivalent to
+		///    <paramref name="value" />.
+		/// </returns>
+		public static implicit operator ReadOnlyByteVector (string value)
+		{
+			return new ReadOnlyByteVector (ByteVector.FromString (
+				value, StringType.UTF8));
+		}
+		
+		#endregion
+		
+		
+		
+		#region IList<T>
+		
+		/// <summary>
+		///    Gets whether or not the current instance is read-only.
+		/// </summary>
+		/// <value>
+		///    Always <see langword="true" />.
+		/// </value>
+		public override bool IsReadOnly {
+			get {return true;}
+		}
+		
+		/// <summary>
+		///    Gets whether or not the current instance is fixed size.
+		/// </summary>
+		/// <value>
+		///    Always <see langword="true" />.
+		/// </value>
+		public override bool IsFixedSize {
+			get {return true;}
+		}
+		
+		#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Riff/AviHeaderList.cs b/lib/TagLib/TagLib/Riff/AviHeaderList.cs
new file mode 100644
index 0000000..ad77abe
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/AviHeaderList.cs
@@ -0,0 +1,364 @@
+//
+// AviHeaderList.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace TagLib.Riff
+{
+	/// <summary>
+	///    This class provides support for reading an AVI header list to
+	///    extract stream information.
+	/// </summary>
+	public class AviHeaderList
+	{
+		/// <summary>
+		///    Contains the AVI header.
+		/// </summary>
+		AviHeader header;
+		
+		/// <summary>
+		///    Contains the AVI codec information.
+		/// </summary>
+		List<ICodec> codecs = new List<ICodec> ();
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AviHeaderList" /> by reading the contents of a raw
+		///    RIFF list from a specified position in a <see
+		///    cref="TagLib.File"/>.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    from which the contents of the new instance is to be
+		///    read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the list.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The list does not contain an AVI header or the AVI header
+		///    is the wrong length.
+		/// </exception>
+		public AviHeaderList (TagLib.File file, long position,
+		                      int length)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (length < 0)
+				throw new ArgumentOutOfRangeException (
+					"length");
+			
+			if (position < 0 || position > file.Length - length)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			List list = new List (file, position, length);
+			
+			if (!list.ContainsKey ("avih"))
+				throw new CorruptFileException (
+					"Avi header not found.");
+			
+			ByteVector header_data = list ["avih"][0];
+			if (header_data.Count != 0x38)
+				throw new CorruptFileException (
+					"Invalid header length.");
+			
+			header = new AviHeader (header_data, 0);
+			
+			foreach (ByteVector list_data in list ["LIST"])
+				if (list_data.StartsWith ("strl"))
+					codecs.Add (AviStream
+						.ParseStreamList (list_data)
+						.Codec);
+		}
+		
+		/// <summary>
+		///    Gets the header for the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="AviHeader" /> object containing the header
+		///    for the current instance.
+		/// </value>
+		public AviHeader Header {
+			get {return header;}
+		}
+		
+		/// <summary>
+		///    Gets the codecs contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ICodec[]" /> containing the codecs contained
+		///    in the current instance.
+		/// </value>
+		public ICodec [] Codecs {
+			get {return codecs.ToArray ();}
+		}
+	}
+	
+	/// <summary>
+	///    This structure provides a representation of a Microsoft
+	///    AviMainHeader structure, minus the first 8 bytes.
+	/// </summary>
+	public struct AviHeader
+	{
+		/// <summary>
+		///    Contains the number of microseconds per frame.
+		/// </summary>
+		uint microseconds_per_frame;
+
+		/// <summary>
+		///    Contains the maximum number of bytes per second.
+		/// </summary>
+		uint max_bytes_per_second;
+
+		/// <summary>
+		///    Contains the flags.
+		/// </summary>
+		uint flags;
+
+		/// <summary>
+		///    Contains the total number of frames.
+		/// </summary>
+		uint total_frames;
+		
+		/// <summary>
+		///    Contains the number of initial frames.
+		/// </summary>
+		uint initial_frames;
+		
+		/// <summary>
+		///    Contains the number of streams.
+		/// </summary>
+		uint streams;
+		
+		/// <summary>
+		///    Contains the suggested buffer size.
+		/// </summary>
+		uint suggested_buffer_size;
+		
+		/// <summary>
+		///    Contains the video width.
+		/// </summary>
+		uint width;
+		
+		/// <summary>
+		///    Contains the video height.
+		/// </summary>
+		uint height;
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AviHeader" /> by reading the raw structure from the
+		///    beginning of a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 40 bytes.
+		/// </exception>
+		[Obsolete("Use AviHeader(ByteVector,int)")]
+		public AviHeader (ByteVector data) : this (data, 0)
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AviStreamHeader" /> by reading the raw structure
+		///    from a specified position in a <see cref="ByteVector" />
+		///    object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the index in
+		///    <paramref name="data"/> at which the structure begins.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 40 bytes at
+		///    <paramref name="offset" />.
+		/// </exception>
+		public AviHeader (ByteVector data, int offset)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException (
+					"offset");
+			
+			if (offset + 40 > data.Count)
+				throw new CorruptFileException (
+					"Expected 40 bytes.");
+			
+			microseconds_per_frame = data.Mid (offset,      4).ToUInt (false);
+			max_bytes_per_second   = data.Mid (offset +  4, 4).ToUInt (false);
+			flags                  = data.Mid (offset + 12, 4).ToUInt (false);
+			total_frames           = data.Mid (offset + 16, 4).ToUInt (false);
+			initial_frames         = data.Mid (offset + 20, 4).ToUInt (false);
+			streams                = data.Mid (offset + 24, 4).ToUInt (false);
+			suggested_buffer_size  = data.Mid (offset + 28, 4).ToUInt (false);
+			width                  = data.Mid (offset + 32, 4).ToUInt (false);
+			height                 = data.Mid (offset + 36, 4).ToUInt (false);
+		}
+		
+		/// <summary>
+		///    Gets the number of microseconds per frame.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying number of
+		///    microseconds per frame.
+		/// </value>
+		public uint MicrosecondsPerFrame {
+			get {return microseconds_per_frame;}
+		}
+		
+		/// <summary>
+		///    Gets the maximum number of bytes per second.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying maximum number of
+		///    bytes per second.
+		/// </value>
+		public uint MaxBytesPerSecond {
+			get {return max_bytes_per_second;}
+		}
+		
+		/// <summary>
+		///    Gets the file flags.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying file flags.
+		/// </value>
+		public uint Flags {
+			get {return flags;}
+		}
+		
+		/// <summary>
+		///    Gets the number of frames in the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the number of
+		///    frames in the file.
+		/// </value>
+		public uint TotalFrames {
+			get {return total_frames;}
+		}
+		
+		/// <summary>
+		///    Gets how far ahead audio is from video.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying how far ahead
+		///    audio is from video.
+		/// </value>
+		public uint InitialFrames {
+			get {return initial_frames;}
+		}
+		
+		/// <summary>
+		///    Gets the number of streams in the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the number of
+		///    streams in the file.
+		/// </value>
+		public uint Streams {
+			get {return streams;}
+		}
+		
+		/// <summary>
+		///    Gets the suggested buffer size for the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the buffer size.
+		/// </value>
+		public uint SuggestedBufferSize {
+			get {return suggested_buffer_size;}
+		}
+		
+		/// <summary>
+		///    Gets the width of the video in the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the width of the
+		///    video.
+		/// </value>
+		public uint Width {
+			get {return width;}
+		}
+		
+		/// <summary>
+		///    Gets the height of the video in the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the height of the
+		///    video.
+		/// </value>
+		public uint Height {
+			get {return height;}
+		}
+		
+		/// <summary>
+		///    Gets the duration of the media in the file.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> value containing the duration
+		///    of the file.
+		/// </value>
+		public TimeSpan Duration {
+			get {
+				return TimeSpan.FromMilliseconds (
+					(double) TotalFrames *
+					(double) MicrosecondsPerFrame / 1000.0);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Riff/AviStream.cs b/lib/TagLib/TagLib/Riff/AviStream.cs
new file mode 100644
index 0000000..41143f8
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/AviStream.cs
@@ -0,0 +1,595 @@
+//
+// AviStream.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Text;
+
+namespace TagLib.Riff
+{
+	/// <summary>
+	///    This abstract class provides basic support for parsing a raw AVI
+	///    stream list.
+	/// </summary>
+	public abstract class AviStream
+	{
+		/// <summary>
+		///    Contains the stream header.
+		/// </summary>
+		private AviStreamHeader header;
+		
+		/// <summary>
+		///    Contains the stream codec information.
+		/// </summary>
+		private ICodec codec;
+		
+		/// <summary>
+		///    Constructs and intializes a new instance of <see
+		///    cref="AviStream" /> with a specified stream header.
+		/// </summary>
+		/// <param name="header">
+		///   A <see cref="AviStreamHeader"/> object containing the
+		///   stream's header.
+		/// </param>
+		protected AviStream (AviStreamHeader header)
+		{
+			this.header = header;
+		}
+		
+		/// <summary>
+		///    Parses a stream list item.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the item's
+		///    ID.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the item's
+		///    data.
+		/// </param>
+		/// <param name="start">
+		///    A <see cref="uint" /> value specifying the index in
+		///    <paramref name="data" /> at which the item data begins.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="uint" /> value specifying the length of the
+		///    item.
+		/// </param>
+		public virtual void ParseItem (ByteVector id, ByteVector data,
+		                               int start, int length)
+		{
+		}
+		
+		/// <summary>
+		///    Gets the stream header.
+		/// </summary>
+		/// <value>
+		///    A <see cref="AviStreamHeader" /> object containing the
+		///    header information for the stream.
+		/// </value>
+		public AviStreamHeader Header {
+			get {return header;}
+		}
+		
+		/// <summary>
+		///    Gets the codec information.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ICodec" /> object containing the codec
+		///    information for the stream.
+		/// </value>
+		public ICodec Codec {
+			get {return codec;}
+			protected set {this.codec = value;}
+		}
+		
+		/// <summary>
+		///    Parses a raw AVI stream list and returns the stream
+		///    information.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing raw stream
+		///    list.
+		/// </param>
+		/// <returns>
+		///    A <see cref="AviStream" /> object containing stream
+		///    information.
+		/// </returns>
+		public static AviStream ParseStreamList (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+		
+			
+			if (!data.StartsWith ("strl"))
+				return null;
+			
+			AviStream stream = null;
+			int pos = 4;
+			
+			while (pos + 8 < data.Count) {
+				ByteVector id = data.Mid (pos, 4);
+				int block_length = (int) data.Mid (pos + 4, 4)
+					.ToUInt (false);
+				
+				if (id == "strh" && stream == null) {
+					AviStreamHeader stream_header =
+						new AviStreamHeader (data, pos + 8);
+					if (stream_header.Type == "vids")
+						stream = new AviVideoStream (
+							stream_header);
+					else if (stream_header.Type == "auds")
+						stream = new AviAudioStream (
+							stream_header);
+				} else if (stream != null) {
+					stream.ParseItem (id, data, pos + 8, block_length);
+				}
+				
+				pos += block_length + 8;
+			}
+			
+			return stream;
+		}
+	}
+	
+	/// <summary>
+	///    This class extends <see cref="AviStream" /> to provide support
+	///    for reading audio stream data.
+	/// </summary>
+	public class AviAudioStream : AviStream
+	{
+		/// <summary>
+		///    Constructs and intializes a new instance of <see
+		///    cref="AviAudioStream" /> with a specified stream header.
+		/// </summary>
+		/// <param name="header">
+		///   A <see cref="AviStreamHeader"/> object containing the
+		///   stream's header.
+		/// </param>
+		public AviAudioStream (AviStreamHeader header)
+			: base (header)
+		{
+		}
+		
+		/// <summary>
+		///    Parses a stream list item.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the item's
+		///    ID.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the item's
+		///    data.
+		/// </param>
+		/// <param name="start">
+		///    A <see cref="uint" /> value specifying the index in
+		///    <paramref name="data" /> at which the item data begins.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="uint" /> value specifying the length of the
+		///    item.
+		/// </param>
+		public override void ParseItem (ByteVector id, ByteVector data,
+		                                int start, int length)
+		{
+			if (id == "strf")
+				Codec = new WaveFormatEx (data, start);
+		}
+	}
+	
+	/// <summary>
+	///    This class extends <see cref="AviStream" /> to provide support
+	///    for reading video stream data.
+	/// </summary>
+	public class AviVideoStream : AviStream
+	{
+		/// <summary>
+		///    Constructs and intializes a new instance of <see
+		///    cref="AviVideoStream" /> with a specified stream header.
+		/// </summary>
+		/// <param name="header">
+		///   A <see cref="AviStreamHeader"/> object containing the
+		///   stream's header.
+		/// </param>
+		public AviVideoStream (AviStreamHeader header)
+			: base (header)
+		{
+		}
+		
+		/// <summary>
+		///    Parses a stream list item.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the item's
+		///    ID.
+		/// </param>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the item's
+		///    data.
+		/// </param>
+		/// <param name="start">
+		///    A <see cref="uint" /> value specifying the index in
+		///    <paramref name="data" /> at which the item data begins.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="uint" /> value specifying the length of the
+		///    item.
+		/// </param>
+		public override void ParseItem (ByteVector id, ByteVector data,
+		                                int start, int length)
+		{
+			if (id == "strf")
+				Codec = new BitmapInfoHeader (data, start);
+		}
+	}
+	
+	/// <summary>
+	///    This structure provides a representation of a Microsoft
+	///    AviStreamHeader structure, minus the first 8 bytes.
+	/// </summary>
+	public struct AviStreamHeader
+	{
+		/// <summary>
+		///    Contains the stream type.
+		/// </summary>
+		private ByteVector type;
+		
+		/// <summary>
+		///    Contains the stream handler.
+		/// </summary>
+		private ByteVector handler;
+		
+		/// <summary>
+		///    Contains the flags.
+		/// </summary>
+		private uint flags;
+		
+		/// <summary>
+		///    Contains the priority.
+		/// </summary>
+		private uint priority;
+		
+		/// <summary>
+		///    Contains the initial frame count.
+		/// </summary>
+		private uint initial_frames;
+		
+		/// <summary>
+		///    Contains the scale.
+		/// </summary>
+		private uint scale;
+		
+		/// <summary>
+		///    Contains the rate.
+		/// </summary>
+		private uint rate;
+		
+		/// <summary>
+		///    Contains the start delay.
+		/// </summary>
+		private uint start;
+		
+		/// <summary>
+		///    Contains the stream length.
+		/// </summary>
+		private uint length;
+		
+		/// <summary>
+		///    Contains the suggested buffer size.
+		/// </summary>
+		private uint suggested_buffer_size;
+		
+		/// <summary>
+		///    Contains the quality (between 0 and 10,000).
+		/// </summary>
+		private uint quality;
+		
+		/// <summary>
+		///    Contains the sample size.
+		/// </summary>
+		private uint sample_size;
+		
+		/// <summary>
+		///    Contains the position for the left side of the video.
+		/// </summary>
+		private ushort left;
+		
+		/// <summary>
+		///    Contains the position for the top side of the video.
+		/// </summary>
+		private ushort top;
+		
+		/// <summary>
+		///    Contains the position for the right side of the video.
+		/// </summary>
+		private ushort right;
+		
+		/// <summary>
+		///    Contains the position for the bottom side of the video.
+		/// </summary>
+		private ushort bottom;
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AviStreamHeader" /> by reading the raw structure
+		///    from the beginning of a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 56 bytes.
+		/// </exception>
+		[Obsolete("Use WaveFormatEx(ByteVector,int)")]
+		public AviStreamHeader (ByteVector data) : this (data, 0)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="AviStreamHeader" /> by reading the raw structure
+		///    from a specified position in a <see cref="ByteVector" />
+		///    object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the index in
+		///    <paramref name="data"/> at which the structure begins.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 56 bytes at
+		///    <paramref name="offset" />.
+		/// </exception>
+		public AviStreamHeader (ByteVector data, int offset)
+		{
+			if (data == null)
+				throw new System.ArgumentNullException ("data");
+			
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException (
+					"offset");
+			
+			if (offset + 56 > data.Count)
+				throw new CorruptFileException (
+					"Expected 56 bytes.");
+			
+			type                  = data.Mid (offset,      4);
+			handler               = data.Mid (offset +  4, 4);
+			flags                 = data.Mid (offset +  8, 4).ToUInt (false);
+			priority              = data.Mid (offset + 12, 4).ToUInt (false);
+			initial_frames        = data.Mid (offset + 16, 4).ToUInt (false);
+			scale                 = data.Mid (offset + 20, 4).ToUInt (false);
+			rate                  = data.Mid (offset + 24, 4).ToUInt (false);
+			start                 = data.Mid (offset + 28, 4).ToUInt (false);
+			length                = data.Mid (offset + 32, 4).ToUInt (false);
+			suggested_buffer_size = data.Mid (offset + 36, 4).ToUInt (false);
+			quality               = data.Mid (offset + 40, 4).ToUInt (false);
+			sample_size           = data.Mid (offset + 44, 4).ToUInt (false);
+			left                  = data.Mid (offset + 48, 2).ToUShort (false);
+			top                   = data.Mid (offset + 50, 2).ToUShort (false);
+			right                 = data.Mid (offset + 52, 2).ToUShort (false);
+			bottom                = data.Mid (offset + 54, 2).ToUShort (false);
+		}
+		
+		/// <summary>
+		///    Gets the stream type.
+		/// </summary>
+		/// <value>
+		///    A four-byte <see cref="ByteVector" /> object specifying
+		///    stream type.
+		/// </value>
+		public ByteVector Type {
+			get {return type;}
+		}
+		
+		/// <summary>
+		///    Gets the stream handler (codec) ID.
+		/// </summary>
+		/// <value>
+		///    A four-byte <see cref="ByteVector" /> object specifying
+		///    stream handler ID.
+		/// </value>
+		public ByteVector Handler {
+			get {return handler;}
+		}
+		
+		/// <summary>
+		///    Gets the stream flags.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying stream flags.
+		/// </value>
+		public uint Flags {
+			get {return flags;}
+		}
+		
+		/// <summary>
+		///    Gets the stream priority.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying stream priority.
+		/// </value>
+		public uint Priority {
+			get {return priority;}
+		}
+		
+		/// <summary>
+		///    Gets how far ahead audio is from video.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying how far ahead
+		///    audio is from video.
+		/// </value>
+		public uint InitialFrames {
+			get {return initial_frames;}
+		}
+		
+		/// <summary>
+		///    Gets the scale of the stream.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the scale of the
+		///    stream.
+		/// </value>
+		/// <remarks>
+		///    Dividing <see cref="Rate"/> by <see cref="Scale" /> gives
+		///    the number of samples per second.
+		/// </remarks>
+		public uint Scale {
+			get {return scale;}
+		}
+		
+		/// <summary>
+		///    Gets the rate of the stream.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the rate of the
+		///    stream.
+		/// </value>
+		/// <remarks>
+		///    Dividing <see cref="Rate"/> by <see cref="Scale" /> gives
+		///    the number of samples per second.
+		/// </remarks>
+		public uint Rate {
+			get {return rate;}
+		}
+		
+		/// <summary>
+		///    Gets the start delay of the stream.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the start delay of
+		///    the stream.
+		/// </value>
+		public uint Start {
+			get {return start;}
+		}
+		
+		/// <summary>
+		///    Gets the length of the stream.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the length of the
+		///    stream.
+		/// </value>
+		public uint Length {
+			get {return length;}
+		}
+		
+		/// <summary>
+		///    Gets the suggested buffer size for the stream.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the buffer size.
+		/// </value>
+		public uint SuggestedBufferSize {
+			get {return suggested_buffer_size;}
+		}
+		
+		/// <summary>
+		///    Gets the quality of the stream data.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the quality of the
+		///    stream data between 0 and 10,000.
+		/// </value>
+		public uint Quality {
+			get {return quality;}
+		}
+		
+		/// <summary>
+		///    Gets the sample size of the stream data.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value specifying the sample size.
+		/// </value>
+		public uint SampleSize {
+			get {return sample_size;}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the left of the video is to
+		///    be displayed in the rectangle whose width is given in the
+		///    the file's <see cref="AviHeader"/>.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value specifying the left
+		///    position.
+		/// </value>
+		public ushort Left {
+			get {return left;}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the top of the video is to be
+		///    displayed in the rectangle whose height is given in the
+		///    the file's <see cref="AviHeader"/>.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value specifying the top
+		///    position.
+		/// </value>
+		public ushort Top {
+			get {return top;}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the right of the video is to
+		///    be displayed in the rectangle whose width is given in the
+		///    the file's <see cref="AviHeader"/>.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value specifying the right
+		///    position.
+		/// </value>
+		public ushort Right {
+			get {return right;}
+		}
+		
+		/// <summary>
+		///    Gets the position at which the bottom of the video is
+		///    to be displayed in the rectangle whose height is given in
+		///    the file's <see cref="AviHeader"/>.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value specifying the bottom
+		///    position.
+		/// </value>
+		public ushort Bottom {
+			get {return bottom;}
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Riff/BitmapInfoHeader.cs b/lib/TagLib/TagLib/Riff/BitmapInfoHeader.cs
new file mode 100644
index 0000000..08d2239
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/BitmapInfoHeader.cs
@@ -0,0 +1,853 @@
+//
+// BitmapInfoHeader.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Globalization;
+
+namespace TagLib.Riff
+{
+	/// <summary>
+	///    This structure provides a representation of a Microsoft
+	///    BitmapInfoHeader structure.
+	/// </summary>
+	public struct BitmapInfoHeader : IVideoCodec
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the size of the header.
+		/// </summary>
+		uint size; 
+		
+		/// <summary>
+		///    Contains the video width.
+		/// </summary>
+		uint width; 
+		
+		/// <summary>
+		///    Contains the video height.
+		/// </summary>
+		uint height; 
+		
+		/// <summary>
+		///    Contains the number of planes.
+		/// </summary>
+		ushort planes; 
+		
+		/// <summary>
+		///    Contains the bit count.
+		/// </summary>
+		ushort bit_count; 
+		
+		/// <summary>
+		///    Contains the compression (codec) ID.
+		/// </summary>
+		ByteVector compression_id; 
+		
+		/// <summary>
+		///    Contains the size of the image.
+		/// </summary>
+		uint size_of_image; 
+		
+		/// <summary>
+		///    Contains the number of X pixels per meter.
+		/// </summary>
+		uint x_pixels_per_meter; 
+		
+		/// <summary>
+		///    Contains the number of Y pixels per meter.
+		/// </summary>
+		uint y_pixels_per_meter; 
+		
+		/// <summary>
+		///    Contains the number of colors used.
+		/// </summary>
+		uint colors_used; 
+		
+		/// <summary>
+		///    Contains the number of important colors.
+		/// </summary>
+		uint colors_important;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="BitmapInfoHeader" /> by reading the raw structure
+		///    from the beginning of a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 40 bytes.
+		/// </exception>
+		[Obsolete("Use BitmapInfoHeader(ByteVector,int)")]
+		public BitmapInfoHeader (ByteVector data) : this (data, 0)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="BitmapInfoHeader" /> by reading the raw structure
+		///    from a specified position in a <see cref="ByteVector" />
+		///    object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the index in
+		///    <paramref name="data"/> at which the structure begins.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 16 bytes at
+		///    <paramref name="offset" />.
+		/// </exception>
+		public BitmapInfoHeader (ByteVector data, int offset)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (offset + 40 > data.Count)
+				throw new CorruptFileException (
+					"Expected 40 bytes.");
+			
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException (
+					"offset");
+			
+			size               = data.Mid (offset +  0, 4).ToUInt (false);
+			width              = data.Mid (offset +  4, 4).ToUInt (false);
+			height             = data.Mid (offset +  8, 4).ToUInt (false);
+			planes             = data.Mid (offset + 12, 2).ToUShort (false);
+			bit_count          = data.Mid (offset + 14, 2).ToUShort (false);
+			compression_id     = data.Mid (offset + 16, 4);
+			size_of_image      = data.Mid (offset + 20, 4).ToUInt (false);
+			x_pixels_per_meter = data.Mid (offset + 24, 4).ToUInt (false);
+			y_pixels_per_meter = data.Mid (offset + 28, 4).ToUInt (false);
+			colors_used        = data.Mid (offset + 32, 4).ToUInt (false);
+			colors_important   = data.Mid (offset + 36, 4).ToUInt (false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the size of the structure in bytes.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    bytes in the structure.
+		/// </value>
+		public uint HeaderSize {
+			get {return size;}
+		}
+		
+		/// <summary>
+		///    Gets the number of planes in the image.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value containing the number of
+		///    planes.
+		/// </value>
+		public ushort Planes {
+			get {return planes;}
+		}
+		
+		/// <summary>
+		///    Gets the number of bits per pixel.
+		/// </summary>
+		/// <value>
+		///    A <see cref="ushort" /> value containing the number of
+		///    bits per pixel, equivalent to the log base 2 of the
+		///    maximum number of colors.
+		/// </value>
+		public ushort BitCount {
+			get {return bit_count;}
+		}
+		
+		/// <summary>
+		///    Gets the compression ID for image.
+		/// </summary>
+		/// <value>
+		///    A four-byte <see cref="ByteVector" /> object containing
+		///    the ID of the compression system (codec) used by the
+		///    image.
+		/// </value>
+		public ByteVector CompressionId {
+			get {return compression_id;}
+		}
+		
+		/// <summary>
+		///    Gets the size of the image in bytes.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    bytes in the image.
+		/// </value>
+		public uint ImageSize {
+			get {return size_of_image;}
+		}
+		
+		/// <summary>
+		///    Gets the horizontal resolution of the target device.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    pixels-per-meter in the hoizontal direction for the
+		///    target device.
+		/// </value>
+		public uint XPixelsPerMeter {
+			get {return x_pixels_per_meter;}
+		}
+		
+		/// <summary>
+		///    Gets the vertical resolution of the target device.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    pixels-per-meter in the vertical direction for the
+		///    target device.
+		/// </value>
+		public uint YPixelsPerMeter {
+			get {return y_pixels_per_meter;}
+		}
+		
+		/// <summary>
+		///    Gets the number of colors in the image.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    colors.
+		/// </value>
+		public uint ColorsUsed {
+			get {return colors_used;}
+		}
+		
+		/// <summary>
+		///    Gets the number of colors important in displaying the
+		///    image.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> value containing the number of
+		///    important colors.
+		/// </value>
+		public uint ImportantColors {
+			get {return colors_important;}
+		}
+		
+#endregion
+		
+		
+		
+#region IVideoCodec
+		
+		/// <summary>
+		///    Gets the width of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the width of the
+		///    video represented by the current instance.
+		/// </value>
+		public int VideoWidth  {
+			get {return (int)width;}
+		}
+		
+		/// <summary>
+		///    Gets the height of the video represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the height of the
+		///    video represented by the current instance.
+		/// </value>
+		public int VideoHeight {
+			get {return (int)height;}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Video" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Video;}
+		}
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TimeSpan.Zero" />.
+		/// </value>
+		public TimeSpan Duration {
+			get {return TimeSpan.Zero;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {
+				string id = CompressionId.ToString (StringType.UTF8)
+					.ToUpper (CultureInfo.InvariantCulture);
+				switch (id)
+				{
+				case "AEMI":
+					return "Array VideoONE MPEG1-I capture";
+				case "ALPH":
+					return "Ziracom Video";
+				case "AMPG":
+					return "Array VideoONE capture/compression";
+				case "ANIM":
+					return "Intel RDX";
+				case "AP41":
+					return "Microsoft Corporation Video";
+				case "AUR2":
+					return "AuraVision Aura 2 codec";
+				case "AURA":
+					return "AuraVision Aura 1 codec";
+				case "AUVX":
+					return "USH GmbH AUVX video codec";
+				case "BT20":
+					return "Brooktree MediaStream codec";
+				case "BTCV":
+					return "Brooktree composite video codec";
+				case "CC12":
+					return "Intel YUV12 codec";
+				case "CDVC":
+					return "Canopus DV codec";
+				case "CGDI":
+					return "Microsoft CamCorder in Office 97 (screen capture codec)";
+				case "CHAM":
+					return "Winnov Caviara Champagne";
+				case "CM10":
+					return "CyberLink Corporation MediaShow 1.0";
+				case "CPLA":
+					return "Weitek 4:2:0 YUV planar";
+				case "CT10":
+					return "CyberLink Corporation TalkingShow 1.0";
+				case "CVID":
+					return "Cinepak by SuperMac";
+				case "CWLT":
+					return "Microsoft Corporation Video";
+				case "CYUV":
+					return "Creative Labs YUV";
+				case "DIV3":
+				case "MP43":
+					return "Microsoft MPEG-4 Version 3 Video";
+				case "DIV4":
+					return "Microsoft Corporation Video";
+				case "DIVX":
+					return "DivX Video";
+				case "DJPG":
+					return "Broadway 101 Motion JPEG codec";
+				case "DP16":
+					return "YUV411 with DPCM 6-bit compression";
+				case "DP18":
+					return "YUV411 with DPCM 8-bit compression";
+				case "DP26":
+					return "YUV422 with DPCM 6-bit compression";
+				case "DP28":
+					return "YUV422 with DPCM 8-bit compression";
+				case "DP96":
+					return "YVU9 with DPCM 6-bit compression";
+				case "DP98":
+					return "YVU9 with DPCM 8-bit compression";
+				case "DP9L":
+					return "YVU9 with DPCM 6-bit compression and thinned-out";
+				case "DUCK":
+					return "The Duck Corporation TrueMotion 1.0";
+				case "DV25":
+					return "SMPTE 314M 25Mb/s compressed";
+				case "DV50":
+					return "SMPTE 314M 50Mb/s compressed";
+				case "DVE2":
+					return "DVE-2 videoconferencing codec";
+				case "DVH1":
+					return "DVC Pro HD";
+				case "DVHD":
+					return "DV data as defined in Part 3 of the Specification of Consumer-use Digital VCRs";
+				case "DVNM":
+					return "Matsushita Electric Industrial Co., Ltd. Video";
+				case "DVSD":
+					return "DV data as defined in Part 2 of the Specification of Consumer-use Digital VCRs";
+				case "DVSL":
+					return "DV data as defined in Part 6 of Specification of Consumer-use Digital VCRs";
+				case "DVX1":
+					return "Lucent DVX1000SP video decoder.";
+				case "DVX2":
+					return "Lucent DVX2000S video decoder";
+				case "DVX3":
+					return "Lucent DVX3000S video decoder";
+				case "DXTC":
+					return "DirectX texture compression";
+				case "DX50":
+					return "DivX Version 5 Video";
+				case "EMWC":
+					return "EverAd Marquee WMA codec";
+				case "ETV1":
+				case "ETV2":
+				case "ETVC":
+					return "eTreppid video codec";
+				case "FLJP":
+					return "Field-encoded motion JPEG with LSI bitstream format";
+				case "FRWA":
+					return "Softlab-Nsk Ltd. Forward alpha";
+				case "FRWD":
+					return "Softlab-Nsk Ltd. Forward JPEG";
+				case "FRWT":
+					return "Softlab-Nsk Ltd. Forward JPEG+alpha";
+				case "FVF1":
+					return "Iterated Systems, Inc. Fractal video frame";
+				case "FXT1":
+					return "3dfx Interactive, Inc. Video";
+				case "GWLT":
+					return "Microsoft Corporation Video";
+				case "H260":
+				case "H261":
+				case "H262":
+				case "H263":
+				case "H264":
+				case "H265":
+				case "H266":
+				case "H267":
+				case "H268":
+				case "H269":
+					return "Intel " + CompressionId.ToString (StringType.UTF8) + " Conferencing codec";
+				case "I263":
+					return "Intel I263";
+				case "I420":
+					return "Intel Indeo 4 codec";
+				case "IAN":
+					return "Intel RDX";
+				case "ICLB":
+					return "InSoft, Inc. CellB videoconferencing codec";
+				case "IFO9":
+					return "Intel intermediate YUV9";
+				case "ILVC":
+					return "Intel layered Video";
+				case "ILVR":
+					return "ITU-T's H.263+ compression standard";
+				case "IMAC":
+					return "Intel hardware motion compensation";
+				case "IPDV":
+					return "IEEE 1394 digital video control and capture board format";
+				case "IRAW":
+					return "Intel YUV uncompressed";
+				case "ISME":
+					return "Intel's next-generation video codec";
+				case "IUYV":
+					return "UYVY interlaced (even, then odd lines)";
+				case "IV30":
+				case "IV31":
+				case "IV32":
+				case "IV33":
+				case "IV34":
+				case "IV35":
+				case "IV36":
+				case "IV37":
+				case "IV38":
+				case "IV39":
+					return "Intel Indeo Video Version 3";
+				case "IV40":
+				case "IV41":
+				case "IV42":
+				case "IV43":
+				case "IV44":
+				case "IV45":
+				case "IV46":
+				case "IV47":
+				case "IV48":
+				case "IV49":
+					return "Intel Indeo Video Version 4";
+				case "IV50":
+					return "Intel Indeo Video Version 5";
+				case "IY41":
+					return "LEAD Technologies, Inc. Y41P interlaced (even, then odd lines)";
+				case "IYU1":
+					return "IEEE 1394 Digital Camera 1.04 Specification: mode 2, 12-bit YUV (4:1:1)";
+				case "IYU2":
+					return "IEEE 1394 Digital Camera 1.04 Specification: mode 2, 24 bit YUV (4:4:4)";
+				case "JPEG":
+					return "Microsoft Corporation Still image JPEG DIB.";
+				case "LEAD":
+					return "LEAD Technologies, Inc. Proprietary MCMP compression";
+				case "LIA1":
+					return "Liafail";
+				case "LJPG":
+					return "LEAD Technologies, Inc. Lossless JPEG compression";
+				case "LSV0":
+					return "Infinop Inc. Video";
+				case "LSVC":
+					return "Infinop Lightning Strike constant bit rate video codec";
+				case "LSVW":
+					return "Infinop Lightning Strike multiple bit rate video codec";
+				case "M101":
+					return "Matrox Electronic Systems, Ltd. Uncompressed field-based YUY2";
+				case "M4S2":
+					return "Microsoft ISO MPEG-4 video V1.1";
+				case "MJPG":
+					return "Motion JPEG";
+				case "MMES":
+					return "Matrox MPEG-2 elementary video stream";
+				case "MMIF":
+					return "Matrox MPEG-2 elementary I-frame-only video stream";
+				case "MP2A":
+					return "Media Excel Inc. MPEG-2 audio";
+				case "MP2T":
+					return "Media Excel Inc. MPEG-2 transport";
+				case "MP2V":
+					return "Media Excel Inc. MPEG-2 video";
+				case "MP42":
+					return "Microsoft MPEG-4 video codec V2";
+				case "MP4A":
+					return "Media Excel Inc. MPEG-4 audio";
+				case "MP4S":
+					return "Microsoft ISO MPEG-4 video V1.0";
+				case "MP4T":
+					return "Media Excel Inc. MPEG-4 transport";
+				case "MP4V":
+					return "Media Excel Inc. MPEG-4 video";
+				case "MPEG":
+					return "Chromatic Research, Inc. MPEG-1 video, I frame";
+				case "MPG4":
+					return "Microsoft MPEG-4 Version 1 Video";
+				case "MRCA":
+					return "FAST Multimedia AG Mrcodec";
+				case "MRLE":
+					return "Microsoft Run length encoding";
+				case "MSS1":
+					return "Microsoft screen codec V1";
+				case "MSV1":
+					return "Microsoft video codec V1";
+				case "MSVC":
+					return "Microsoft Video 1";
+				case "MV10":
+				case "MV11":
+				case "MV12":
+				case "MV99":
+				case "MVC1":
+				case "MVC2":
+				case "MVC9":
+					return "Nokia MVC video codec";
+				case "NTN1":
+					return "Nogatech video compression 1";
+				case "NY12":
+					return "Nogatech YUV 12 format";
+				case "NYUV":
+					return "Nogatech YUV 422 format";
+				case "PCL2":
+					return "Pinnacle RL video codec";
+				case "PCLE":
+					return "Pinnacle Studio 400 video codec";
+				case "PHMO":
+					return "IBM Corporation Photomotion";
+				case "QPEG":
+					return "Q-Team QPEG 1.1 format video codec";
+				case "RGBT":
+					return "Computer Concepts Ltd. 32-bit support";
+				case "RIVA":
+					return "NVIDIA Corporation Swizzled texture format";
+				case "RLND":
+					return "Roland Corporation Video";
+				case "RT21":
+					return "Intel Indeo 2.1";
+				case "RVX":
+					return "Intel RDX";
+				case "S263":
+					return "Sorenson Vision H.263";
+				case "SCCD":
+					return "Luminositi SoftCam codec";
+				case "SDCC":
+					return "Sun Digital Camera codec";
+				case "SFMC":
+					return "Crystal Net SFM codec";
+				case "SMSC":
+				case "SMSD":
+					return "Radius Proprietary";
+				case "SPLC":
+					return "Splash Studios ACM audio codec";
+				case "SQZ2":
+					return "Microsoft VXtreme video codec V2";
+				case "STVA":
+					return "ST CMOS Imager Data (Bayer)";
+				case "STVB":
+					return "ST CMOS Imager Data (Nudged Bayer)";
+				case "STVC":
+					return "ST CMOS Imager Data (Bunched)";
+				case "SV10":
+					return "Sorenson Video R1";
+				case "SV3M":
+					return "Sorenson SV3 module decoder";
+				case "TLMS":
+				case "TLST":
+					return "TeraLogic motion intraframe codec";
+				case "TM20":
+					return "The Duck Corporation TrueMotion 2.0";
+				case "TMIC":
+					return "TeraLogic motion intraframe codec";
+				case "TMOT":
+					return "TrueMotion video compression algorithm";
+				case "TR20":
+					return "The Duck Corporation TrueMotion RT 2.0";
+				case "ULTI":
+					return "IBM Corporation Ultimotion";
+				case "UYVP":
+					return "Evans & Sutherland YCbCr 4:2:2 extended precision, 10 bits per component (U0Y0V0Y1)";
+				case "V261":
+					return "Lucent VX3000S video codec";
+				case "V422":
+					return "VITEC Multimedia 24-bit YUV 4:2:2 format (CCIR 601)";
+				case "V655":
+					return "VITEC Multimedia 16-bit YUV 4:2:2 format";
+				case "VCR1":
+					return "ATI VCR 1.0";
+				case "VCWV":
+					return "VideoCon wavelet";
+				case "VDCT":
+					return "VITEC Multimedia Video Maker Pro DIB";
+				case "VIDS":
+					return "VITEC Multimedia YUV 4:2:2 CCIR 601 for v422";
+				case "VGPX":
+					return "Alaris VGPixel video";
+				case "VIVO":
+					return "Vivo H.263 video codec";
+				case "VIXL":
+					return "miro Computer Products AG";
+				case "VJPG":
+					return "Video Communication Systems - A JPEG-based compression scheme for RGB bitmaps";
+				case "VLV1":
+					return "VideoLogic Systems VLCAP.DRV";
+				case "VQC1":
+					return "ViewQuest Technologies Inc. 0x31435156";
+				case "VQC2":
+					return "ViewQuest Technologies Inc. 0x32435156";
+				case "VQJP":
+					return "ViewQuest Technologies Inc. VQ630 dual-mode digital camera";
+				case "VQS4":
+					return "ViewQuest Technologies Inc. VQ110 digital video camera";
+				case "VX1K":
+					return "Lucent VX1000S video codec";
+				case "VX2K":
+					return "Lucent VX2000S video codec";
+				case "VXSP":
+					return "Lucent VX1000SP video codec9";
+				case "WBVC":
+					return "Winbond Electronics Corporation W9960";
+				case "WINX":
+					return "Winnov, Inc. Video";
+				case "WJPG":
+					return "Winbond motion JPEG bitstream format";
+				case "WMV1":
+					return "Microsoft Windows Media Video Version 7";
+				case "WMV2":
+					return "Microsoft Windows Media Video Version 8";
+				case "WMV3":
+					return "Microsoft Windows Media Video Version 9";
+				case "WNV1":
+				case "WPY2":
+					return "Winnov, Inc. Video";
+				case "WZCD":
+					return "CORE Co. Ltd. iScan";
+				case "WZDC":
+					return "CORE Co. Ltd. iSnap";
+				case "XJPG":
+					return "Xirlink JPEG-like compressor";
+				case "XLV0":
+					return "XL video decoder";
+				case "XVID":
+					return "XviD Video";
+				case "YC12":
+					return "Intel YUV12 Video";
+				case "YCCK":
+					return "Uncompressed YCbCr Video with key data";
+				case "YU92":
+					return "Intel YUV Video";
+				case "YUV8":
+					return "Winnov Caviar YUV8 Video";
+				case "YUV9":
+					return "Intel YUV Video";
+				case "YUYP":
+					return "Evans & Sutherland YCbCr 4:2:2 extended precision, 10 bits per component Video";
+				case "YUYV":
+					return "Canopus YUYV Compressor Video";
+				case "ZPEG":
+					return "Metheus Corporation Video Zipper";
+				case "ZPG1":
+				case "ZPG2":
+				case "ZPG3":
+				case "ZPG4":
+					return "VoDeo Solutions Video";
+				default:
+					return string.Format (
+						CultureInfo.InvariantCulture,
+						"Unknown Image ({0})",
+						CompressionId);
+				}
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region IEquatable
+		
+		/// <summary>
+		///    Generates a hash code for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value containing the hash code for
+		///    the current instance.
+		/// </returns>
+		public override int GetHashCode ()
+		{
+			unchecked {
+				return (int) (size ^ width ^ height ^ planes ^
+					bit_count ^ compression_id.ToUInt () ^
+					size_of_image ^ x_pixels_per_meter ^
+					y_pixels_per_meter ^ colors_used ^
+					colors_important);
+			}
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another object.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="object" /> to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public override bool Equals (object other)
+		{
+			if (!(other is BitmapInfoHeader))
+				return false;
+			
+			return Equals ((BitmapInfoHeader) other);
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another instance of <see cref="BitmapInfoHeader" />.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="BitmapInfoHeader" /> object to compare to
+		///    the current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public bool Equals (BitmapInfoHeader other)
+		{
+			return size == other.size && width == other.width &&
+				height == other.height && planes == other.planes &&
+				bit_count == other.bit_count &&
+				compression_id == other.compression_id &&
+				size_of_image == other.size_of_image &&
+				x_pixels_per_meter == other.x_pixels_per_meter &&
+				y_pixels_per_meter == other.y_pixels_per_meter &&
+				colors_used == other.colors_used &&
+				colors_important == other.colors_important;
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="WaveFormatEx" /> are equal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="BitmapInfoHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="BitmapInfoHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    equal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator == (BitmapInfoHeader first,
+		                                BitmapInfoHeader second)
+		{
+			return first.Equals (second);
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="BitmapInfoHeader" /> differ.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="BitmapInfoHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="BitmapInfoHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    unequal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator != (BitmapInfoHeader first,
+		                                BitmapInfoHeader second)
+		{
+			return !first.Equals (second);
+		}
+#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Riff/DivXTag.cs b/lib/TagLib/TagLib/Riff/DivXTag.cs
new file mode 100644
index 0000000..822c43a
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/DivXTag.cs
@@ -0,0 +1,406 @@
+//
+// DivXTag.cs: Provide support for reading and writing DivX tags.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   TagLib.Id3v1.Tag
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System.Collections;
+using System;
+using System.Text;
+using System.Globalization;
+
+namespace TagLib.Riff {
+	/// <summary>
+	///    This class extends <see cref="Tag" /> to provide support for
+	///    reading and writing tags stored in the DivX format.
+	/// </summary>
+	public class DivXTag : TagLib.Tag
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the title.
+		/// </summary>
+		private string title;
+		
+		/// <summary>
+		///    Contains the semicolon separated performers.
+		/// </summary>
+		private string artist;
+		
+		/// <summary>
+		///    Contains the 4 digit year.
+		/// </summary>
+		private string year;
+		
+		/// <summary>
+		///    Contains a comment on track.
+		/// </summary>
+		private string comment;
+		
+		/// <summary>
+		///    Contains the genre index.
+		/// </summary>
+		private string genre;
+		
+		/// <summary>
+		///    Contains the extra 6 bytes at the end of the tag.
+		/// </summary>
+		private ByteVector extra_data;
+		
+#endregion
+
+
+
+
+#region Public Static Fields
+		
+		/// <summary>
+		///    The size of a DivX tag.
+		/// </summary>
+		public const uint Size = 128;
+		
+		/// <summary>
+		///    The identifier used to recognize a DivX tags.
+		/// </summary>
+		/// <value>
+		///    "DIVXTAG"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "DIVXTAG";
+
+#endregion
+
+
+
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DivXTag" /> with no contents.
+		/// </summary>
+		public DivXTag ()
+		{
+			Clear ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DivXTag" /> by reading the contents from a
+		///    specified position in a specified file.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="File" /> object containing the file from
+		///    which the contents of the new instance is to be read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the tag.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The file does not contain the file identifier at the
+		///    correct offset from the given position.
+		/// </exception>
+		public DivXTag (File file, long position)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			file.Mode = TagLib.File.AccessMode.Read;
+			
+			if (position < 0 ||
+				position > file.Length - Size)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			file.Seek (position);
+			
+			// read the tag -- always 128 bytes
+			
+			ByteVector data = file.ReadBlock ((int) Size);
+			
+			// some initial sanity checking
+			
+			if (!data.EndsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"DivX tag data does not end with identifier.");
+			
+			Parse (data);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="DivXTag" /> by reading the contents raw tag data
+		///    stored in a specified <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> containing a raw DivX tag to
+		///    read into the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    The file does not contain the file identifier at the
+		///    correct offset from the given position.
+		/// </exception>
+		public DivXTag (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"DivX tag data is less than 128 bytes long.");
+			
+			if (!data.EndsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"DivX tag data does not end with identifier.");
+			
+			Parse (data);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw DivX tag.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector" /> object containing the
+		///    rendered tag.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			data.Add (ByteVector.FromString (title,   StringType.Latin1).Resize (32, 0x20));
+			data.Add (ByteVector.FromString (artist,  StringType.Latin1).Resize (28, 0x20));
+			data.Add (ByteVector.FromString (year,    StringType.Latin1).Resize ( 4, 0x20));
+			data.Add (ByteVector.FromString (comment, StringType.Latin1).Resize (48, 0x20));
+			data.Add (ByteVector.FromString (genre,   StringType.Latin1).Resize ( 3, 0x20));
+			data.Add (extra_data);
+			data.Add (FileIdentifier);
+			return data;
+		}
+		
+#endregion
+		
+		
+		
+#region Private Methods
+		
+		/// <summary>
+		///    Populates the current instance by parsing the contents of
+		///    a raw DivX tag.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the
+		///    starting with an DivX tag.
+		/// </param>
+		private void Parse (ByteVector data)
+		{
+			title      = data.ToString (StringType.Latin1,  0, 32).Trim ();
+			artist     = data.ToString (StringType.Latin1, 32, 28).Trim ();
+			year       = data.ToString (StringType.Latin1, 60,  4).Trim ();
+			comment    = data.ToString (StringType.Latin1, 64, 48).Trim ();
+			genre      = data.ToString (StringType.Latin1,112,  3).Trim ();
+			extra_data = data.Mid (115,  6);
+		}
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.Id3v1" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.DivX;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    When stored on disk, only the first 32 bytes of the
+		///    Latin-1 encoded value will be stored. This may result in
+		///    lost data.
+		/// </remarks>
+		public override string Title {
+			get {
+				return string.IsNullOrEmpty (title) ?
+					null : title;
+			}
+			set {
+				title = value != null ?
+					value.Trim () : String.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    When stored on disk, only the first 28 bytes of the
+		///    Latin-1 encoded value will be stored, minus a byte for
+		///    each additionial performer (i.e. two performers will only
+		///    have 27 bytes and three performers will only have 26
+		///    bytes).This may result in lost data.
+		/// </remarks>
+		public override string [] Performers {
+			get {
+				return string.IsNullOrEmpty (artist) ?
+					new string [0] : artist.Split (';');
+			}
+			set {
+				artist = value != null ?
+					string.Join (";", value) : string.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    When stored on disk, only the first 48 bytes of the
+		///    Latin-1 encoded value will be stored. This may result in
+		///    lost data.
+		/// </remarks>
+		public override string Comment {
+			get {
+				return string.IsNullOrEmpty (comment) ?
+					null : comment;
+			}
+			set {
+				comment = value != null ?
+					value.Trim () : String.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    Only first genre will be stored and only if it is an
+		///    exact match for a value appearing in <see
+		///    cref="TagLib.Genres.Video" />. All other values will
+		///    result in the property being cleared.
+		/// </remarks>
+		public override string [] Genres {
+			get {
+				string genre_name =
+					TagLib.Genres.IndexToVideo (genre);
+				
+				return (genre_name != null) ?
+					new string [] {genre_name} :
+					new string [0];
+			}
+			set {
+				genre = (value != null && value.Length > 0) ?
+					TagLib.Genres.VideoToIndex (
+						value [0].Trim ()).ToString (
+							CultureInfo.InvariantCulture)
+					: string.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    Only values between 1 and 9999 will be stored, all other
+		///    values will result in the property being zeroed.
+		/// </remarks>
+		public override uint Year {
+			get {
+				uint value;
+				return uint.TryParse (year,
+					NumberStyles.Integer,
+					CultureInfo.InvariantCulture,
+					out value) ? value : 0;
+			}
+			
+			set {
+				year = (value > 0 && value < 10000) ?
+					value.ToString (
+						CultureInfo.InvariantCulture) :
+					String.Empty;
+			}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			title = artist = genre = year = comment = String.Empty;
+			extra_data = new ByteVector (6);
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Riff/File.cs b/lib/TagLib/TagLib/Riff/File.cs
new file mode 100644
index 0000000..80ed717
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/File.cs
@@ -0,0 +1,644 @@
+//
+// File.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace TagLib.Riff
+{
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide
+	///    support for reading and writing tags and properties for files
+	///    using the RIFF file format such as AVI and Wave files.
+	/// </summary>
+	[SupportedMimeType("taglib/avi", "avi")]
+	[SupportedMimeType("taglib/wav", "wav")]
+	[SupportedMimeType("taglib/divx", "divx")]
+	[SupportedMimeType("video/avi")]
+	[SupportedMimeType("video/msvideo")]
+	[SupportedMimeType("video/x-msvideo")]
+	[SupportedMimeType("image/avi")]
+	[SupportedMimeType("application/x-troff-msvideo")]
+	[SupportedMimeType("audio/avi")]
+	[SupportedMimeType("audio/wav")]
+	[SupportedMimeType("audio/wave")]
+	[SupportedMimeType("audio/x-wav")]
+	public class File : TagLib.File
+	{
+#region Private Fields
+		
+		/// <summary>
+		///  Contains all the tags of the file.
+		/// </summary>
+		private CombinedTag tag = new CombinedTag ();
+		
+		/// <summary>
+		///  Contains the INFO tag.
+		/// </summary>
+		private InfoTag info_tag = null;
+		
+		/// <summary>
+		///  Contains the MovieID tag.
+		/// </summary>
+		private MovieIdTag mid_tag = null;
+		
+		/// <summary>
+		///  Contains the DivX tag.
+		/// </summary>
+		private DivXTag divx_tag = null;
+		
+		/// <summary>
+		///  Contains the Id3v2 tag.
+		/// </summary>
+		private Id3v2.Tag id32_tag = null;
+		
+		/// <summary>
+		///  Contains the media properties.
+		/// </summary>
+		private Properties properties = null;
+		
+#endregion
+		
+		
+		
+#region Public Static Fields
+		
+		/// <summary>
+		///    The identifier used to recognize a RIFF files.
+		/// </summary>
+		/// <value>
+		///    "RIFF"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "RIFF";
+		
+#endregion
+		
+		
+		
+#region Public Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: this (new File.LocalFileAbstraction (path),
+			        propertiesStyle)
+		{
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : this (path, ReadStyle.Average)
+		{
+		}
+      
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle) : base (abstraction)
+		{
+			uint riff_size;
+			long tag_start, tag_end;
+
+			Mode = AccessMode.Read;
+			try {
+				Read (true, propertiesStyle, out riff_size,
+					out tag_start, out tag_end);
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+
+			TagTypesOnDisk = TagTypes;
+
+			GetTag (TagTypes.Id3v2, true);
+			GetTag (TagTypes.RiffInfo, true);
+			GetTag (TagTypes.MovieId, true);
+			GetTag (TagTypes.DivX, true);
+		}
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: this (abstraction, ReadStyle.Average)
+		{}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets a abstract representation of all tags stored in the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Tag" /> object representing all tags
+		///    stored in the current instance.
+		/// </value>
+		public override Tag Tag {
+			get {return tag;}
+		}
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties {
+			get {return properties;}
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			Mode = AccessMode.Write;
+			try {
+				ByteVector data = new ByteVector ();
+				
+				// Enclose the Id3v2 tag in an "ID32" item and
+				// embed it as the first tag.
+				if (id32_tag != null) {
+					ByteVector tag_data = id32_tag.Render ();
+					if (tag_data.Count > 10) {
+						if (tag_data.Count % 2 == 1)
+							tag_data.Add (0);
+						data.Add ("ID32");
+						data.Add (ByteVector.FromUInt (
+							(uint) tag_data.Count,
+							false));
+						data.Add (tag_data);
+					}
+				}
+				
+				// Embed "INFO" as the second tag.
+				if (info_tag != null)
+					data.Add (info_tag.RenderEnclosed ());
+
+				// Embed "MID " as the third tag.
+				if (mid_tag != null)
+					data.Add (mid_tag.RenderEnclosed ());
+
+				// Embed the DivX tag in "IDVX and embed it as
+				// the fourth tag.
+				if (divx_tag != null && !divx_tag.IsEmpty) {
+					ByteVector tag_data = divx_tag.Render ();
+					data.Add ("IDVX");
+					data.Add (ByteVector.FromUInt (
+						(uint) tag_data.Count, false));
+					data.Add (tag_data);
+				}
+				
+				// Read the file to determine the current RIFF
+				// size and the area tagging does in.
+				uint riff_size;
+				long tag_start, tag_end;
+				Read (false, ReadStyle.None, out riff_size,
+					out tag_start, out tag_end);
+				
+				// If tagging info cannot be found, place it at
+				// the end of the file.
+				if (tag_start < 12 || tag_end < tag_start)
+					tag_start = tag_end = Length;
+				
+				int length = (int)(tag_end - tag_start);
+				
+				// If the tag isn't at the end of the file,
+				// try appending using padding to improve
+				// write time now or for subsequent writes.
+				if (tag_end != Length) {
+					int padding_size = length - data.Count - 8;
+					if (padding_size < 0)
+						padding_size = 1024;
+					
+					
+					data.Add ("JUNK");
+					data.Add (ByteVector.FromUInt (
+						(uint)padding_size, false));
+					data.Add (new ByteVector (padding_size));
+				}
+				
+				// Insert the tagging data.
+				Insert (data, tag_start, length);
+				
+				// If the data size changed, and the tagging
+				// data is within the RIFF portion of the file,
+				// update the riff size.
+				if (data.Count - length != 0 &&
+					tag_start <= riff_size)
+					Insert (ByteVector.FromUInt ((uint)
+						(riff_size + data.Count - length),
+						false), 4, 4);
+				
+				// Update the tag types.
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Removes a set of tag types from the current instance.
+		/// </summary>
+		/// <param name="types">
+		///    A bitwise combined <see cref="TagLib.TagTypes" /> value
+		///    containing tag types to be removed from the file.
+		/// </param>
+		/// <remarks>
+		///    In order to remove all tags from a file, pass <see
+		///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
+		/// </remarks>
+		public override void RemoveTags (TagTypes types)
+		{
+			if ((types & TagLib.TagTypes.Id3v2) != TagLib.TagTypes.None)
+				id32_tag = null;
+			if ((types & TagLib.TagTypes.RiffInfo) != TagLib.TagTypes.None)
+				info_tag = null;
+			if ((types & TagLib.TagTypes.MovieId) != TagLib.TagTypes.None)
+				mid_tag  = null;
+			if ((types & TagLib.TagTypes.DivX) != TagLib.TagTypes.None)
+				divx_tag = null;
+
+			tag.SetTags (id32_tag, info_tag, mid_tag, divx_tag);
+		}
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			TagLib.Tag tag = null;
+
+			switch (type)
+			{
+			case TagTypes.Id3v2:
+				if (id32_tag == null && create) {
+					id32_tag = new Id3v2.Tag ();
+					id32_tag.Version = 4;
+					id32_tag.Flags |= Id3v2.HeaderFlags
+						.FooterPresent;
+					this.tag.CopyTo (id32_tag, true);
+				}
+				
+				tag = id32_tag;
+				break;
+				
+			case TagTypes.RiffInfo:
+				if (info_tag == null && create) {
+					info_tag = new InfoTag ();
+					this.tag.CopyTo (info_tag, true);
+				}
+				
+				tag = info_tag;
+				break;
+				
+			case TagTypes.MovieId:
+				if (mid_tag == null && create) {
+					mid_tag = new MovieIdTag ();
+					this.tag.CopyTo (mid_tag, true);
+				}
+				
+				tag = mid_tag;
+				break;
+				
+			case TagTypes.DivX:
+				if (divx_tag == null && create) {
+					divx_tag = new DivXTag ();
+					this.tag.CopyTo (divx_tag, true);
+				}
+				
+				tag = divx_tag;
+				break;
+			}
+
+			this.tag.SetTags (id32_tag, info_tag, mid_tag, divx_tag);
+			return tag;
+		}
+		
+#endregion
+		
+		
+		
+#region Private Methods
+		
+		/// <summary>
+		///    Reads the contents of the current instance determining
+		///    the size of the riff data, the area the tagging is in,
+		///    and optionally reading in the tags and media properties.
+		/// </summary>
+		/// <param name="read_tags">
+		///    If <see langword="true" />, any tags found will be read
+		///    into the current instance.
+		/// </param>
+		/// <param name="style">
+		///    A <see cref="ReadStyle"/> value specifying how the media
+		///    data is to be read into the current instance.
+		/// </param>
+		/// <param name="riff_size">
+		///    A <see cref="uint"/> value reference to be filled with
+		///    the size of the RIFF data as read from the file.
+		/// </param>
+		/// <param name="tag_start">
+		///    A <see cref="long" /> value reference to be filled with
+		///    the absolute seek position at which the tagging data
+		///    starts.
+		/// </param>
+		/// <param name="tag_end">
+		///    A <see cref="long" /> value reference to be filled with
+		///    the absolute seek position at which the tagging data
+		///    ends.
+		/// </param>
+		/// <exception cref="CorruptFileException">
+		///    The file does not begin with <see cref="FileIdentifier"
+		///    />.
+		/// </exception>
+		private void Read (bool read_tags, ReadStyle style,
+		                   out uint riff_size, out long tag_start,
+		                   out long tag_end)
+		{
+			Seek (0);
+			if (ReadBlock (4) != FileIdentifier)
+				throw new CorruptFileException (
+					"File does not begin with RIFF identifier");
+			
+			riff_size = ReadBlock (4).ToUInt (false);
+			ByteVector stream_format = ReadBlock (4);
+			tag_start = -1;
+			tag_end   = -1;
+			
+			long position = 12;
+			long length = Length;
+			uint size = 0;
+			TimeSpan duration = TimeSpan.Zero;
+			ICodec [] codecs = new ICodec [0];
+			
+			// Read until there are less than 8 bytes to read.
+			do {
+				bool tag_found = false;
+				
+				Seek (position);
+				string fourcc = ReadBlock (4).ToString (StringType.UTF8);
+				size = ReadBlock (4).ToUInt (false);
+				
+				switch (fourcc)
+				{
+				
+				// "fmt " is used by Wave files to hold the
+				// WaveFormatEx structure.
+				case "fmt ":
+					if (style == ReadStyle.None ||
+						stream_format != "WAVE")
+						break;
+					
+					Seek (position + 8);
+					codecs = new ICodec [] {
+						new WaveFormatEx (ReadBlock (18), 0)
+					};
+					break;
+				
+				// "data" contains the audio data for wave
+				// files. It's contents represent the invariant
+				// portion of the file and is used to determine
+				// the duration of a file. It should always
+				// appear after "fmt ".
+				case "data":
+					if (stream_format != "WAVE")
+						break;
+					
+					InvariantStartPosition = position;
+					InvariantEndPosition = position + size;
+					
+					if (style == ReadStyle.None ||
+						codecs.Length != 1 ||
+						!(codecs [0] is WaveFormatEx))
+						break;
+					
+					duration += TimeSpan.FromSeconds (
+						(double) size / (double)
+						((WaveFormatEx) codecs [0])
+							.AverageBytesPerSecond);
+					
+					break;
+				
+				// Lists are used to store a variety of data
+				// collections. Read the type and act on it.
+				case "LIST":
+				{
+					switch (ReadBlock (4).ToString (StringType.UTF8))
+					{
+					
+					// "hdlr" is used by AVI files to hold
+					// a media header and BitmapInfoHeader
+					// and WaveFormatEx structures.
+					case "hdrl":
+						if (style == ReadStyle.None ||
+							stream_format != "AVI ")
+							continue;
+						
+						AviHeaderList header_list =
+							new AviHeaderList (this,
+								position + 12,
+								(int) (size - 4));
+						duration = header_list.Header.Duration;
+						codecs = header_list.Codecs;
+						break;
+					
+					// "INFO" is a tagging format handled by
+					// the InfoTag class.
+					case "INFO":
+						if (read_tags && info_tag == null)
+							info_tag = new InfoTag (
+								this,
+								position + 12,
+								(int) (size - 4));
+						
+						tag_found = true;
+						break;
+					
+					// "MID " is a tagging format handled by
+					// the MovieIdTag class.
+					case "MID ":
+						if (read_tags && mid_tag == null)
+							mid_tag = new MovieIdTag (
+								this,
+								position + 12,
+								(int) (size - 4));
+						
+						tag_found = true;
+						break;
+					
+					// "movi" contains the media data for
+					// and AVI and its contents represent
+					// the invariant portion of the file.
+					case "movi":
+						if (stream_format != "AVI ")
+							break;
+						
+						InvariantStartPosition = position;
+						InvariantEndPosition = position + size;
+						break;
+					}
+					break;
+				}
+				
+				// "ID32" is a custom box for this format that
+				// contains an ID3v2 tag.
+				case "ID32":
+					if (read_tags && id32_tag == null)
+						id32_tag = new Id3v2.Tag (this,
+							position + 8);
+					
+					tag_found = true;
+					break;
+				
+				// "IDVX" is used by DivX and holds an ID3v1-
+				// style tag.
+				case "IDVX":
+					if (read_tags && divx_tag == null)
+						divx_tag = new DivXTag (this,
+							position + 8);
+					
+					tag_found = true;
+					break;
+				
+				// "JUNK" is a padding element that could be
+				// associated with tag data.
+				case "JUNK":
+					if (tag_end == position)
+						tag_end = position + 8 + size;
+					break;
+				}
+				
+				// Determine the region of the file that
+				// contains tags.
+				if (tag_found) {
+					if (tag_start == -1) {
+						tag_start = position;
+						tag_end = position + 8 + size;
+					} else if (tag_end == position) {
+						tag_end = position + 8 + size;
+					}
+				}
+				
+				// Move to the next item.
+			} while ((position += 8 + size) + 8 < length);
+			
+			// If we're reading properties, and one were found,
+			// throw an exception. Otherwise, create the Properties
+			// object.
+			if (style != ReadStyle.None) {
+				if (codecs.Length == 0)
+					throw new UnsupportedFormatException (
+						"Unsupported RIFF type.");
+				
+				properties = new Properties (duration, codecs);
+			}
+			
+			// If we're reading tags, update the combined tag.
+			if (read_tags)
+				tag.SetTags (id32_tag, info_tag, mid_tag, divx_tag);
+		}
+		
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Riff/InfoTag.cs b/lib/TagLib/TagLib/Riff/InfoTag.cs
new file mode 100644
index 0000000..2bdeeea
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/InfoTag.cs
@@ -0,0 +1,313 @@
+//
+// InfoTag.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+namespace TagLib.Riff
+{
+	/// <summary>
+	///    This class extends <see cref="ListTag" /> to provide support for
+	///    reading and writing standard INFO tags.
+	/// </summary>
+	public class InfoTag : ListTag
+	{
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="InfoTag" /> with no contents.
+		/// </summary>
+		public InfoTag () : base ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="InfoTag" /> by reading the contents of a raw
+		///    RIFF list stored in a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> containing a raw RIFF list to
+		///    read into the new instance.
+		/// </param>
+		public InfoTag (ByteVector data) : base (data)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="InfoTag" /> by reading the contents of a raw RIFF
+		///    list from a specified position in a <see
+		///    cref="TagLib.File"/>.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    from which the contents of the new instance is to be
+		///    read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the list.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		public InfoTag (TagLib.File file, long position, int length)
+			: base (file, position, length)
+		{
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance enclosed in a "INFO" item.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		public override ByteVector RenderEnclosed ()
+		{
+		return RenderEnclosed ("INFO");
+		}
+		
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.RiffInfo" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.RiffInfo;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "INAM" item.
+		/// </remarks>
+		public override string Title {
+			get {
+				foreach (string s in GetValuesAsStrings ("INAM"))
+					if (!string.IsNullOrEmpty (s))
+						return s;
+				
+				return null;
+			}
+			set {SetValue ("INAM", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ISTR" item.
+		/// </remarks>
+		public override string [] Performers {
+			get {return GetValuesAsStrings ("ISTR");}
+			set {SetValue ("ISTR", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "IART" item.
+		/// </remarks>
+		public override string [] AlbumArtists {
+			get {return GetValuesAsStrings ("IART");}
+			set {SetValue ("IART", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "IWRI" item.
+		/// </remarks>
+		public override string [] Composers {
+			get {return GetValuesAsStrings ("IWRI");}
+			set {SetValue ("IWRI", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ICMT" item.
+		/// </remarks>
+		public override string Comment {
+			get {
+				foreach (string s in GetValuesAsStrings ("ICMT"))
+					if (!string.IsNullOrEmpty (s))
+						return s;
+				
+				return null;
+			}
+			set {SetValue ("ICMT", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "IGNR" item.
+		/// </remarks>
+		public override string [] Genres {
+			get {return GetValuesAsStrings ("IGNR");}
+			set {SetValue ("IGNR", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ICRD" item.
+		/// </remarks>
+		public override uint Year {
+			get {return GetValueAsUInt ("ICRD");}
+			set {SetValue ("ICRD", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "IPRT" item.
+		/// </remarks>
+		public override uint Track {
+			get {return GetValueAsUInt ("IPRT");}
+			set {SetValue ("IPRT", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "IFRM" item.
+		/// </remarks>
+		public override uint TrackCount {
+			get {return GetValueAsUInt ("IFRM");}
+			set {SetValue ("IFRM", value);}
+		}
+
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "ICOP" item.
+		/// </remarks>
+		public override string Copyright {
+			get {
+				foreach (string s in GetValuesAsStrings ("ICOP"))
+					if (!string.IsNullOrEmpty (s))
+						return s;
+				
+				return null;
+			}
+			set {SetValue ("ICOP", value);}
+		}
+#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Riff/List.cs b/lib/TagLib/TagLib/Riff/List.cs
new file mode 100644
index 0000000..bbb0c96
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/List.cs
@@ -0,0 +1,610 @@
+//
+// List.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace TagLib.Riff {
+	/// <summary>
+	///    This class extends <see
+	///    cref="T:System.Collections.Generic.Dictionary`2" /> to provide
+	///    support for reading and writing RIFF lists.
+	/// </summary>
+	[Serializable]
+	[ComVisible(false)]
+	public class List : Dictionary <ByteVector,ByteVectorCollection>
+	{
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="List" /> with no contents.
+		/// </summary>
+		public List ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="List" /> by reading the contents of a raw RIFF
+		///    list stored in a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> containing a raw RIFF list to
+		///    read into the new instance.
+		/// </param>
+		public List (ByteVector data)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			Parse (data);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="List" /> by reading the contents of a raw RIFF list
+		///    from a specified position in a <see cref="TagLib.File"/>.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    from which the contents of the new instance is to be
+		///    read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the list.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		public List (TagLib.File file, long position, int length)
+		{
+			if (file == null)
+				throw new ArgumentNullException ("file");
+			
+			if (length < 0)
+				throw new ArgumentOutOfRangeException (
+					"length");
+			
+			if (position < 0 || position > file.Length - length)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			file.Seek (position);
+			Parse (file.ReadBlock (length));
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="List" /> from a specified serialization info and
+		///    streaming context.
+		/// </summary>
+		/// <param name="info">
+		///    A <see cref="SerializationInfo" /> object containing the
+		///    serialized data to be used for the new instance.
+		/// </param>
+		/// <param name="context">
+		///    A <see cref="StreamingContext" /> object containing the
+		///    streaming context information for the new instance.
+		/// </param>
+		/// <remarks>
+		///    This constructor is implemented because <see
+		///    cref="List" /> implements the <see cref="ISerializable"
+		///    /> interface.
+		/// </remarks>
+		protected List (SerializationInfo info,
+		                StreamingContext context)
+			: base (info, context)
+		{
+		}
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance as a raw RIFF list.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			ByteVector data = new ByteVector ();
+			
+			foreach (ByteVector id in Keys)
+				foreach (ByteVector value in this [id]) {
+					if (value.Count == 0)
+						continue;
+					
+					data.Add (id);
+					data.Add (ByteVector.FromUInt (
+						(uint) value.Count, false));
+					data.Add (value);
+					
+					if (value.Count % 2 == 1)
+						data.Add (0);
+				}
+			
+			return data;
+		}
+		
+		/// <summary>
+		///    Renders the current instance enclosed in an item with a
+		///    specified ID.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector"/> object containing the ID of
+		///    the item to enclose the current instance in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public ByteVector RenderEnclosed (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			ByteVector data = Render ();
+			
+			if (data.Count <= 8)
+				return new ByteVector ();
+			
+			ByteVector header = new ByteVector ("LIST");
+			header.Add (ByteVector.FromUInt (
+				(uint) (data.Count + 4), false));
+			header.Add (id);
+			data.Insert (0, header);
+			return data;
+		}
+		
+		/// <summary>
+		///    Gets the values for a specified item in the current
+		///    instance as a <see cref="ByteVectorCollection" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    the values of the specified item.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public ByteVectorCollection GetValues (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			ByteVectorCollection value;
+			
+			return TryGetValue (id, out value) ?
+				value : new ByteVectorCollection ();
+		}
+		
+		/// <summary>
+		///    Gets the values for a specified item in the current
+		///    instance as a <see cref="string[]" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the values of the
+		///    specified item.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public string [] GetValuesAsStrings (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			ByteVectorCollection values = GetValues (id);
+			
+			string [] result = new string [values.Count];
+			
+			for (int i = 0; i < result.Length; i ++) {
+				ByteVector data = values [i];
+				
+				if (data == null) {
+					result [i] = string.Empty;
+					continue;
+				}
+				
+				int length = data.Count;
+				while (length > 0 && data [length - 1] == 0)
+					length --;
+				
+				result [i] = data
+					.ToString (StringType.UTF8, 0, length);
+			}
+			
+			return result;
+		}
+		
+		/// <summary>
+		///    Gets the values for a specified item in the current
+		///    instance as a <see cref="StringCollection" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="StringCollection" /> object containing the
+		///    values of the specified item.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		[Obsolete("Use GetValuesAsStrings(ByteVector)")]
+		public StringCollection GetValuesAsStringCollection (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			return new StringCollection (GetValuesAsStrings (id));
+		}
+		
+		/// <summary>
+		///    Gets the value for a specified item in the current
+		///    instance as a <see cref="uint"/>.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the first value
+		///    with the specified ID that could be converted to an
+		///    integer, or zero if none could be found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public uint GetValueAsUInt (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			foreach (string text in GetValuesAsStrings (id)) {
+				uint value;
+				if (uint.TryParse (text, out value))
+					return value;
+			}
+			
+			return 0;
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see
+		///    cref="T:System.Collections.Generic.IEnumerable`1" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1"
+		///    /> containing the <see cref="ByteVector"/> objects to
+		///    store in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id,
+		                      IEnumerable<ByteVector> values)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			if (values == null)
+				RemoveValue (id);
+			else if (ContainsKey (id))
+				this [id] = new ByteVectorCollection (values);
+			else
+				Add (id, new ByteVectorCollection (values));
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see cref="ByteVector[]"
+		///    />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="ByteVector[]" /> containing the values to
+		///    store in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, params ByteVector [] values)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			if (values == null || values.Length == 0)
+				RemoveValue (id);
+			else
+				SetValue (id, values as IEnumerable<ByteVector>);
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the value of a <see cref="uint"/>.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="uint" /> value to store in the specified
+		///    item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, uint value)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			if (value == 0)
+				RemoveValue (id);
+			else
+				SetValue (id, value.ToString (
+					CultureInfo.InvariantCulture));
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see
+		///    cref="T:System.Collections.Generic.IEnumerable`1" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1"
+		///    /> containing the <see cref="string"/> objects to store
+		///    in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, IEnumerable<string> values)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			if (values == null) {
+				RemoveValue (id);
+				return;
+			}
+			
+			ByteVectorCollection l = new ByteVectorCollection ();
+			foreach (string value in values) {
+				if (string.IsNullOrEmpty (value))
+					continue;
+				
+				ByteVector data = ByteVector.FromString (value,
+					StringType.UTF8);
+				data.Add (0);
+				l.Add (data);
+			}
+			
+			if (l.Count == 0)
+				RemoveValue (id);
+			else
+				SetValue (id, l);
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see cref="string[]" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="string[]" /> containing the values to store
+		///    in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, params string [] values)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			if (values == null || values.Length == 0)
+				RemoveValue (id);
+			else
+				SetValue (id, values as IEnumerable<string>);
+		}
+		
+		/// <summary>
+		///    Removes the item with the specified ID from the current
+		///    instance.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector"/> object containing the ID of
+		///    the item to remove from the current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void RemoveValue (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			if (ContainsKey (id))
+				Remove (id);
+		}
+		
+#endregion
+		
+		
+		
+#region Private Methods
+		
+		/// <summary>
+		///    Populates the current instance by reading in the contents
+		///    of a raw RIFF list stored in a <see cref="ByteVector" />
+		///    object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> containing a raw RIFF list to
+		///    read into the current instance.
+		/// </param>
+		private void Parse (ByteVector data)
+		{
+			int offset = 0;
+			while (offset + 8 < data.Count) {
+				ByteVector id = data.Mid (offset, 4);
+				int length = (int) data.Mid (offset + 4, 4)
+					.ToUInt (false);
+				
+				if (!ContainsKey (id))
+					Add (id, new ByteVectorCollection ());
+				
+				this [id].Add (data.Mid (offset + 8, length));
+				
+				if (length % 2 == 1)
+					length ++;
+				
+				offset += 8 + length;
+			}
+		}
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Riff/ListTag.cs b/lib/TagLib/TagLib/Riff/ListTag.cs
new file mode 100644
index 0000000..d5ea3da
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/ListTag.cs
@@ -0,0 +1,497 @@
+//
+// ListTag.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Riff
+{
+	/// <summary>
+	///    This abstract class extends <see cref="Tag" /> to provide support
+	///    for reading and writing tags stored in the RIFF list format.
+	/// </summary>
+	public abstract class ListTag : Tag
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the <see cref="List" /> object.
+		/// </summary>
+		List fields;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ListTag" /> with no contents.
+		/// </summary>
+		protected ListTag ()
+		{
+			fields = new List ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MovieIdTag" /> using a specified RIFF list.
+		/// </summary>
+		/// <param name="fields">
+		///    A <see cref="List"/> object to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="fields" /> is <see langword="null" />.
+		/// </exception>
+		protected ListTag (List fields)
+		{
+			if (fields == null)
+				throw new ArgumentNullException ("fields");
+			
+			this.fields = fields;
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ListTag" /> by reading the contents of a raw
+		///    RIFF list stored in a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> containing a raw RIFF list to
+		///    read into the new instance.
+		/// </param>
+		protected ListTag (ByteVector data)
+		{
+			fields = new List (data);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="ListTag" /> by reading the contents of a raw RIFF
+		///    list from a specified position in a <see
+		///    cref="TagLib.File" />.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    from which the contents of the new instance is to be
+		///    read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the list.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		protected ListTag (TagLib.File file, long position, int length)
+		{
+			if (file == null)
+				throw new System.ArgumentNullException ("file");
+         
+			if (length < 0)
+				throw new ArgumentOutOfRangeException (
+					"length");
+			
+			if (position < 0 || position > file.Length - length)
+				throw new ArgumentOutOfRangeException (
+					"position");
+			
+			fields = new List (file, position, length);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance enclosed in the appropriate
+		///    item.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		public abstract ByteVector RenderEnclosed ();
+		
+		/// <summary>
+		///    Renders the current instance enclosed in an item with a
+		///    specified ID.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector"/> object containing the ID of
+		///    the item to enclose the current instance in.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		protected ByteVector RenderEnclosed (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			return fields.RenderEnclosed (id);
+		}
+		
+		/// <summary>
+		///    Renders the current instance as a raw RIFF list.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		public ByteVector Render ()
+		{
+			return fields.Render ();
+		}
+		
+		/// <summary>
+		///    Gets the values for a specified item in the current
+		///    instance as a <see cref="ByteVectorCollection" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    the values of the specified item.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public ByteVectorCollection GetValues (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			return fields.GetValues (id);
+		}
+		
+		/// <summary>
+		///    Gets the values for a specified item in the current
+		///    instance as a <see cref="string[]" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="string[]" /> containing the values of the
+		///    specified item.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public string [] GetValuesAsStrings (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			return fields.GetValuesAsStrings (id);
+		}
+		
+		/// <summary>
+		///    Gets the values for a specified item in the current
+		///    instance as a <see cref="StringCollection" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="StringCollection" /> object containing the
+		///    values of the specified item.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		[Obsolete("Use GetValuesAsStrings(ByteVector)")]
+		public StringCollection GetValuesAsStringCollection (ByteVector id)
+		{
+			return new StringCollection (
+				fields.GetValuesAsStrings (id));
+		}
+		
+		/// <summary>
+		///    Gets the value for a specified item in the current
+		///    instance as a <see cref="uint"/>.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <returns>
+		///    A <see cref="uint" /> value containing the first value
+		///    with the specified ID that could be converted to an
+		///    integer, or zero if none could be found.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public uint GetValueAsUInt (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			return fields.GetValueAsUInt (id);
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see cref="ByteVector[]"
+		///    />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ByteVector[]" /> containing the values to
+		///    store in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, params ByteVector [] value)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			fields.SetValue (id, value);
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see
+		///    cref="ByteVectorCollection" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    the values to store in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, ByteVectorCollection value)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			fields.SetValue (id, value);
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the value of a <see cref="uint"/>.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="uint" /> value to store in the specified
+		///    item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, uint value)
+		{
+			fields.SetValue (id, value);
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see
+		///    cref="StringCollection" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="StringCollection" /> object containing the
+		///    values to store in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		[Obsolete("Use SetValue(ByteVector,string[])")]
+		public void SetValue (ByteVector id, StringCollection value)
+		{
+			fields.SetValue (id, value);
+		}
+		
+		/// <summary>
+		///    Sets the value for a specified item in the current
+		///    instance to the contents of a <see cref="string[]" />.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector" /> object containing the ID of
+		///    the item to set.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="string[]" /> containing the values to store
+		///    in the specified item.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void SetValue (ByteVector id, params string [] value)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			fields.SetValue (id, value);
+		}
+		
+		/// <summary>
+		///    Removes the item with the specified ID from the current
+		///    instance.
+		/// </summary>
+		/// <param name="id">
+		///    A <see cref="ByteVector"/> object containing the ID of
+		///    the item to remove from the current instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="id" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentException">
+		///    <paramref name="id" /> isn't exactly four bytes long.
+		/// </exception>
+		public void RemoveValue (ByteVector id)
+		{
+			if (id == null)
+				throw new ArgumentNullException ("id");
+			
+			if (id.Count != 4)
+				throw new ArgumentException (
+					"ID must be 4 bytes long.", "id");
+			
+			fields.RemoveValue (id);
+		}
+		
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance does not
+		///    any values. Otherwise <see langword="false" />.
+		/// </value>
+		public override bool IsEmpty {
+			get {return fields.Count == 0;}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			fields.Clear ();
+		}
+		
+#endregion
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/Riff/MovieIdTag.cs b/lib/TagLib/TagLib/Riff/MovieIdTag.cs
new file mode 100644
index 0000000..48785be
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/MovieIdTag.cs
@@ -0,0 +1,237 @@
+//
+// MovieIdTag.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Riff {
+	/// <summary>
+	///    This class extends <see cref="ListTag" /> to provide support for
+	///    reading and writing MovieID tags.
+	/// </summary>
+	public class MovieIdTag : ListTag
+	{
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MovieIdTag" /> with no contents.
+		/// </summary>
+		public MovieIdTag () : base ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MovieIdTag" /> by reading the contents of a raw
+		///    RIFF list stored in a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector"/> containing a raw RIFF list to
+		///    read into the new instance.
+		/// </param>
+		public MovieIdTag (ByteVector data) : base (data)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="MovieIdTag" /> by reading the contents of a raw
+		///    RIFF list from a specified position in a <see
+		///    cref="TagLib.File"/>.
+		/// </summary>
+		/// <param name="file">
+		///    A <see cref="TagLib.File" /> object containing the file
+		///    from which the contents of the new instance is to be
+		///    read.
+		/// </param>
+		/// <param name="position">
+		///    A <see cref="long" /> value specify at what position to
+		///    read the list.
+		/// </param>
+		/// <param name="length">
+		///    A <see cref="int" /> value specifying the number of bytes
+		///    to read.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="file" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="position" /> is less than zero or greater
+		///    than the size of the file.
+		/// </exception>
+		public MovieIdTag (TagLib.File file, long position, int length)
+			: base (file, position, length)
+		{
+		}
+		
+#endregion
+		
+		
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Renders the current instance enclosed in a "MID " item.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ByteVector"/> object containing the rendered
+		///    version of the current instance.
+		/// </returns>
+		public override ByteVector RenderEnclosed ()
+		{
+			return RenderEnclosed ("MID ");
+		}
+		
+#endregion
+		
+		
+		
+#region TagLib.Tag
+		
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.MovieId" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.MovieId;}
+		}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "TITL" item.
+		/// </remarks>
+		public override string Title {
+			get {
+				foreach (string s in GetValuesAsStrings ("TITL"))
+					if (!string.IsNullOrEmpty (s))
+						return s;
+				
+				return null;
+			}
+			set {SetValue ("TITL", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "IART" item.
+		/// </remarks>
+		public override string [] Performers {
+			get {return GetValuesAsStrings ("IART");}
+			set {SetValue ("IART", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "COMM" item.
+		/// </remarks>
+		public override string Comment {
+			get {
+				foreach (string s in GetValuesAsStrings ("COMM"))
+					if (!string.IsNullOrEmpty (s))
+						return s;
+				
+				return null;
+			}
+			set {SetValue ("COMM", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "GENR" item.
+		/// </remarks>
+		public override string [] Genres {
+			get {return GetValuesAsStrings ("GENR");}
+			set {SetValue ("GENR", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "PRT1" item.
+		/// </remarks>
+		public override uint Track {
+			get {return GetValueAsUInt ("PRT1");}
+			set {SetValue ("PRT1", value);}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    This property is implemented using the "PRT2" item.
+		/// </remarks>
+		public override uint TrackCount {
+			get {return GetValueAsUInt ("PRT2");}
+			set {SetValue ("PRT2", value);}
+		}
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Riff/WaveFormatEx.cs b/lib/TagLib/TagLib/Riff/WaveFormatEx.cs
new file mode 100644
index 0000000..9ea07ce
--- /dev/null
+++ b/lib/TagLib/TagLib/Riff/WaveFormatEx.cs
@@ -0,0 +1,775 @@
+//
+// WaveFormatEx.cs:
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Copyright (C) 2007 Brian Nickel
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Riff {
+	/// <summary>
+	///    This structure provides a representation of a Microsoft
+	///    WaveFormatEx structure.
+	/// </summary>
+	public struct WaveFormatEx : IAudioCodec, ILosslessAudioCodec
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the format tag of the audio.
+		/// </summary>
+		ushort format_tag;
+		
+		/// <summary>
+		///    Contains the number of audio channels.
+		/// </summary>
+		ushort channels;
+		
+		/// <summary>
+		///    Contains the number of samples per second.
+		/// </summary>
+		uint samples_per_second;
+		
+		/// <summary>
+		///    Contains the average number of bytes per second.
+		/// </summary>
+		uint   average_bytes_per_second;
+		
+		/// <summary>
+		///    Contains the number of bits per sample.
+		/// </summary>
+		ushort bits_per_sample;
+		
+#endregion
+		
+		
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="WaveFormatEx" /> by reading the raw structure from
+		///    the beginning of a <see cref="ByteVector" /> object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 16 bytes.
+		/// </exception>
+		[Obsolete("Use WaveFormatEx(ByteVector,int)")]
+		public WaveFormatEx (ByteVector data) : this (data, 0)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="WaveFormatEx" /> by reading the raw structure from
+		///    a specified position in a <see cref="ByteVector" />
+		///    object.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the raw
+		///    data structure.
+		/// </param>
+		/// <param name="offset">
+		///    A <see cref="int" /> value specifying the index in
+		///    <paramref name="data"/> at which the structure begins.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="ArgumentOutOfRangeException">
+		///    <paramref name="offset" /> is less than zero.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> contains less than 16 bytes at
+		///    <paramref name="offset" />.
+		/// </exception>
+		public WaveFormatEx (ByteVector data, int offset)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (offset < 0)
+				throw new ArgumentOutOfRangeException (
+					"offset");
+			
+			if (offset + 16 > data.Count)
+				throw new CorruptFileException (
+					"Expected 16 bytes.");
+			
+			format_tag = data.Mid (offset, 2).ToUShort (false);
+			channels = data.Mid (offset + 2, 2).ToUShort (false);
+			samples_per_second = data.Mid (offset + 4, 4)
+				.ToUInt (false);
+			average_bytes_per_second = data.Mid (offset + 8, 4)
+				.ToUInt (false);
+			bits_per_sample = data.Mid (offset + 14, 2)
+				.ToUShort (false);
+		}
+		
+#endregion
+		
+		
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the format tag of the audio described by the
+		///    current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort" /> value containing the format tag
+		///    of the audio.
+		/// </returns>
+		/// <remarks>
+		///    Format tags indicate the codec of the audio contained in
+		///    the file and are contained in a Microsoft registry. For
+		///    a description of the format, use <see cref="Description"
+		///    />.
+		/// </remarks>
+		public ushort FormatTag {
+			get {return format_tag;}
+		}
+		
+		/// <summary>
+		///    Gets the average bytes per second of the audio described
+		///    by the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort" /> value containing the average
+		///    bytes per second of the audio.
+		/// </returns>
+		public uint AverageBytesPerSecond {
+			get {return average_bytes_per_second;}
+		}
+		
+		/// <summary>
+		///    Gets the bits per sample of the audio described by the
+		///    current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="ushort" /> value containing the bits per
+		///    sample of the audio.
+		/// </returns>
+		public ushort BitsPerSample {
+			get {return bits_per_sample;}
+		}
+		
+#endregion
+
+#region ILosslessAudioCodec
+		
+		int ILosslessAudioCodec.BitsPerSample {
+			get {return bits_per_sample;}
+		}
+		
+#endregion
+		
+#region IAudioCodec
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				return (int) Math.Round (
+					average_bytes_per_second * 8d / 1000d);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {return (int) samples_per_second;}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return channels;}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TimeSpan.Zero" />.
+		/// </value>
+		public TimeSpan Duration {
+			get {return TimeSpan.Zero;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {
+				switch (FormatTag)
+				{
+				case 0x0000:
+					return "Unknown Wave Format";
+				case 0x0001:
+					return "PCM Audio";
+				case 0x0002:
+					return "Microsoft Adaptive PCM Audio";
+				case 0x0003:
+					return "PCM Audio in IEEE floating-point format";
+				case 0x0004:
+					return "Compaq VSELP Audio";
+				case 0x0005:
+					return "IBM CVSD Audio";
+				case 0x0006:
+					return "Microsoft ALAW Audio";
+				case 0x0007:
+					return "Microsoft MULAW Audio";
+				case 0x0008:
+					return "Microsoft DTS Audio";
+				case 0x0009:
+					return "Microsoft DRM Encrypted Audio";
+				case 0x000A:
+					return "Microsoft Speech Audio";
+				case 0x000B:
+					return "Microsoft Windows Media RT Voice Audio";
+				case 0x0010:
+					return "OKI ADPCM Audio";
+				case 0x0011:
+					return "Intel ADPCM Audio";
+				case 0x0012:
+					return "VideoLogic ADPCM Audio";
+				case 0x0013:
+					return "Sierra ADPCM Audio";
+				case 0x0014:
+					return "Antex ADPCM Audio";
+				case 0x0015:
+					return "DSP DIGISTD Audio";
+				case 0x0016:
+					return "DSP DIGIFIX Audio";
+				case 0x0017:
+					return "Dialogic OKI ADPCM Audio";
+				case 0x0018:
+					return "Media Vision ADPCM Audio for Jazz 16";
+				case 0x0019:
+					return "Hewlett-Packard CU Audio";
+				case 0x001A:
+					return "Hewlett-Packard Dynamic Voice Audio";
+				case 0x0020:
+					return "Yamaha ADPCM Audio";
+				case 0x0021:
+					return "Speech Compression Audio";
+				case 0x0022:
+					return "DSP Group True Speech Audio";
+				case 0x0023:
+					return "Echo Speech Audio";
+				case 0x0024:
+					return "Ahead AF36 Audio";
+				case 0x0025:
+					return "Audio Processing Technology Audio";
+				case 0x0026:
+					return "Ahead AF10 Audio";
+				case 0x0027:
+					return "Aculab Prosody CTI Speech Card Audio";
+				case 0x0028:
+					return "Merging Technologies LRC Audio";
+				case 0x0030:
+					return "Dolby AC2 Audio";
+				case 0x0031:
+					return "Microsoft GSM6.10 Audio";
+				case 0x0032:
+					return "Microsoft MSN Audio";
+				case 0x0033:
+					return "Antex ADPCME Audio";
+				case 0x0034:
+					return "Control Resources VQLPC";
+				case 0x0035:
+					return "DSP REAL Audio";
+				case 0x0036:
+					return "DSP ADPCM Audio";
+				case 0x0037:
+					return "Control Resources CR10 Audio";
+				case 0x0038:
+					return "Natural MicroSystems VBXADPCM Audio";
+				case 0x0039:
+					return "Roland RDAC Proprietary Audio Format";
+				case 0x003A:
+					return "Echo Speech Proprietary Audio Compression Format";
+				case 0x003B:
+					return "Rockwell ADPCM Audio";
+				case 0x003C:
+					return "Rockwell DIGITALK Audio";
+				case 0x003D:
+					return "Xebec Proprietary Audio Compression Format";
+				case 0x0040:
+					return "Antex G721 ADPCM Audio";
+				case 0x0041:
+					return "Antex G728 CELP Audio";
+				case 0x0042:
+					return "Microsoft MSG723 Audio";
+				case 0x0043:
+					return "Microsoft MSG723.1 Audio";
+				case 0x0044:
+					return "Microsoft MSG729 Audio";
+				case 0x0045:
+					return "Microsoft SPG726 Audio";
+				case 0x0050:
+					return "Microsoft MPEG Audio";
+				case 0x0052:
+					return "InSoft RT24 Audio";
+				case 0x0053:
+					return "InSoft PAC Audio";
+				case 0x0055:
+					return "ISO/MPEG Layer 3 Audio";
+				case 0x0059:
+					return "Lucent G723 Audio";
+				case 0x0060:
+					return "Cirrus Logic Audio";
+				case 0x0061:
+					return "ESS Technology PCM Audio";
+				case 0x0062:
+					return "Voxware Audio";
+				case 0x0063:
+					return "Canopus ATRAC Audio";
+				case 0x0064:
+					return "APICOM G726 ADPCM Audio";
+				case 0x0065:
+					return "APICOM G722 ADPCM Audio";
+				case 0x0067:
+					return "Microsoft DSAT Display Audio";
+				case 0x0069:
+					return "Voxware Byte Aligned Audio";
+				case 0x0070:
+					return "Voxware AC8 Audio";
+				case 0x0071:
+					return "Voxware AC10 Audio";
+				case 0x0072:
+					return "Voxware AC16 Audio";
+				case 0x0073:
+					return "Voxware AC20 Audio";
+				case 0x0074:
+					return "Voxware RT24 Audio";
+				case 0x0075:
+					return "Voxware RT29 Audio";
+				case 0x0076:
+					return "Voxware RT29HW Audio";
+				case 0x0077:
+					return "Voxware VR12 Audio";
+				case 0x0078:
+					return "Voxware VR18 Audio";
+				case 0x0079:
+					return "Voxware TQ40 Audio";
+				case 0x007A:
+					return "Voxware SC3 Audio";
+				case 0x007B:
+					return "Voxware SC3 Audio";
+				case 0x0080:
+					return "SoftSound Audio";
+				case 0x0081:
+					return "Voxware TQ60 Audio";
+				case 0x0082:
+					return "Microsoft RT24 Audio";
+				case 0x0083:
+					return "AT&T G729A Audio";
+				case 0x0084:
+					return "Motion Pixels MVI2 Audio";
+				case 0x0085:
+					return "Datafusion Systems G726 Audio";
+				case 0x0086:
+					return "Datafusion Systems G610 Audio";
+				case 0x0088:
+					return "Iterated Systems Audio";
+				case 0x0089:
+					return "OnLive! Audio";
+				case 0x008A:
+					return "Multitude FT SX20 Audio";
+				case 0x008B:
+					return "InfoCom ITS ACM G721 Audio";
+				case 0x008C:
+					return "Convedia G729 Audio";
+				case 0x008D:
+					return "Congruency Audio";
+				case 0x0091:
+					return "Siemens Business Communications 24 Audio";
+				case 0x0092:
+					return "Sonic Foundary Dolby AC3 Audio";
+				case 0x0093:
+					return "MediaSonic G723 Audio";
+				case 0x0094:
+					return "Aculab Prosody CTI Speech Card Audio";
+				case 0x0097:
+					return "ZyXEL ADPCM";
+				case 0x0098:
+					return "Philips Speech Processing LPCBB Audio";
+				case 0x0099:
+					return "Studer Professional PACKED Audio";
+				case 0x00A0:
+					return "Malden Electronics Phony Talk Audio";
+				case 0x00A1:
+					return "Racal Recorder GSM Audio";
+				case 0x00A2:
+					return "Racal Recorder G720.a Audio";
+				case 0x00A3:
+					return "Racal G723.1 Audio";
+				case 0x00A4:
+					return "Racal Tetra ACELP Audio";
+				case 0x00B0:
+					return "NEC AAC Audio";
+				case 0x0100:
+					return "Rhetorex ADPCM Audio";
+				case 0x0101:
+					return "BeCubed IRAT Audio";
+				case 0x0111:
+					return "Vivo G723 Audio";
+				case 0x0112:
+					return "Vivo Siren Audio";
+				case 0x0120:
+					return "Philips Speach Processing CELP Audio";
+				case 0x0121:
+					return "Philips Speach Processing GRUNDIG Audio";
+				case 0x0123:
+					return "Digital Equipment Corporation G723 Audio";
+				case 0x0125:
+					return "Sanyo LD-ADPCM Audio";
+				case 0x0130:
+					return "Sipro Lab ACELPNET Audio";
+				case 0x0131:
+					return "Sipro Lab ACELP4800 Audio";
+				case 0x0132:
+					return "Sipro Lab ACELP8v3 Audio";
+				case 0x0133:
+					return "Sipro Lab G729 Audio";
+				case 0x0134:
+					return "Sipro Lab G729A Audio";
+				case 0x0135:
+					return "Sipro Lab KELVIN Audio";
+				case 0x0136:
+					return "VoiceAge AMR Audio";
+				case 0x0140:
+					return "Dictaphone G726 ADPCM Audio";
+				case 0x0141:
+					return "Dictaphone CELP68 Audio";
+				case 0x0142:
+					return "Dictaphone CELP54 Audio";
+				case 0x0150:
+					return "QUALCOMM Pure Voice Audio";
+				case 0x0151:
+					return "QUALCOMM Half Rate Audio";
+				case 0x0155:
+					return "Ring Zero TUBGSM Audio";
+				case 0x0160:
+					return "Microsoft WMA1 Audio";
+				case 0x0161:	
+					return "Microsoft WMA2 Audio";
+				case 0x0162:
+					return "Microsoft Multichannel WMA Audio";
+				case 0x0163:
+					return "Microsoft Lossless WMA Audio";
+				case 0x0170:
+					return "Unisys NAP ADPCM Audio";
+				case 0x0171:
+					return "Unisys NAP ULAW Audio";
+				case 0x0172:
+					return "Unisys NAP ALAW Audio";
+				case 0x0173:
+					return "Unisys NAP 16K Audio";
+				case 0X0174:
+					return "SysCom ACM SYC008 Audio";
+				case 0x0175:
+					return "SysCom ACM SYC701 G726L Audio";
+				case 0x0176:
+					return "SysCom ACM SYC701 CELP54 Audio";
+				case 0x0177:
+					return "SysCom ACM SYC701 CELP68 Audio";
+				case 0x0178:
+					return "Knowledge Adventure ADPCM Audio";
+				case 0x0180:
+					return "MPEG2 AAC Audio";
+				case 0x0190:
+					return "Digital Theater Systems DTS DS Audio";
+				case 0x1979:
+					return "Innings ADPCM Audio";
+				case 0x0200:
+					return "Creative ADPCM Audio";
+				case 0x0202:
+					return "Creative FastSpeech8 Audio";
+				case 0x0203:
+					return "Creative FastSpeech10 Audio";
+				case 0x0210:
+					return "UHER ADPCM Audio";
+				case 0x0220:
+					return "Quarterdeck Audio";
+				case 0x0230:
+					return "I-Link VC Audio";
+				case 0x0240:
+					return "Aureal RAW SPORT Audio";
+				case 0x0250:
+					return "Interactive Prodcuts HSX Audio";
+				case 0x0251:
+					return "Interactive Products RPELP Audio";
+				case 0x0260:
+					return "Consistens Software CS2 Audio";
+				case 0x0270:
+					return "Sony SCX Audio";
+				case 0x0271:
+					return "Sony SCY Audio";
+				case 0x0272:
+					return "Sony ATRAC3 Audio";
+				case 0x0273:
+					return "Sony SPC Audio";
+				case 0x0280:
+					return "Telum Audio";
+				case 0x0281:
+					return "Telum IA Audio";
+				case 0x0285:
+					return "Norcom Voice Systems ADPCM Audio";
+				case 0x0300:
+					return "Fujitsu FM TOWNS SND Audio";
+				case 0x0301:
+				case 0x0302:
+				case 0x0303:
+				case 0x0304:
+				case 0x0305:
+				case 0x0306:
+				case 0x0307:
+				case 0x0308:
+					return "Unknown Fujitsu Audio";
+				case 0x0350:
+					return "Micronas Semiconductors Development Audio";
+				case 0x0351:
+					return "Micronas Semiconductors CELP833 Audio";
+				case 0x0400:
+					return "Brooktree Digital Audio";
+				case 0x0450:
+					return "QDesign Audio";
+				case 0x0680:
+					return "AT&T VME VMPCM Audio";
+				case 0x0681:
+					return "AT&T TPC Audio";
+				case 0x1000:
+					return "Ing. C. Olivetti & C., S.p.A. GSM Audio";
+				case 0x1001:
+					return "Ing. C. Olivetti & C., S.p.A. ADPCM Audio";
+				case 0x1002:
+					return "Ing. C. Olivetti & C., S.p.A. CELP Audio";
+				case 0x1003:
+					return "Ing. C. Olivetti & C., S.p.A. SBC Audio";
+				case 0x1004:
+					return "Ing. C. Olivetti & C., S.p.A. OPR Audio";
+				case 0x1100:
+					return "Lernout & Hauspie Audio";
+				case 0X1101:
+					return "Lernout & Hauspie CELP Audio";
+				case 0X1102:
+					return "Lernout & Hauspie SB8 Audio";
+				case 0X1103:
+					return "Lernout & Hauspie SB12 Audio";
+				case 0X1104:
+					return "Lernout & Hauspie SB16 Audio";
+				case 0x1400:
+					return "Norris Audio";
+				case 0x1500:
+					return "AT&T Soundspace Musicompress Audio";
+				case 0x1971:
+					return "Sonic Foundry Lossless Audio";
+				case 0x2000:
+					return "FAST Multimedia DVM Audio";
+				case 0x4143:
+					return "Divio AAC";
+				case 0x4201:
+					return "Nokia Adaptive Multirate Audio";
+				case 0x4243:
+					return "Divio G726 Audio";
+				case 0x7000:
+					return "3Com NBX Audio";
+				case 0x7A21:
+					return "Microsoft Adaptive Multirate Audio";
+				case 0x7A22:
+					return "Microsoft Adaptive Multirate Audio with silence detection";
+				case 0xA100:
+					return "Comverse Infosys G723 1 Audio";
+				case 0xA101:
+					return "Comverse Infosys AVQSBC Audio";
+				case 0xA102:
+					return "Comverse Infosys OLDSBC Audio";
+				case 0xA103:
+					return "Symbol Technology G729A Audio";
+				case 0xA104:
+					return "VoiceAge AMR WB Audio";
+				case 0xA105:
+					return "Ingenient G726 Audio";
+				case 0xA106:
+					return "ISO/MPEG-4 Advanced Audio Coding";
+				case 0xA107:
+					return "Encore G726 Audio";
+				default:
+					return "Unknown Audio (" + FormatTag + ")";
+				}
+			}
+		}
+		
+#endregion
+		
+		
+		
+#region IEquatable
+		
+		/// <summary>
+		///    Generates a hash code for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value containing the hash code for
+		///    the current instance.
+		/// </returns>
+		public override int GetHashCode ()
+		{
+			unchecked {
+				return (int) (format_tag ^ channels ^
+					samples_per_second ^
+					average_bytes_per_second ^
+					bits_per_sample);
+			}
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another object.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="object" /> to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public override bool Equals (object other)
+		{
+			if (!(other is WaveFormatEx))
+				return false;
+			
+			return Equals ((WaveFormatEx) other);
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another instance of <see cref="WaveFormatEx" />.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="WaveFormatEx" /> object to compare to the
+		///    current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public bool Equals (WaveFormatEx other)
+		{
+			return format_tag == other.format_tag &&
+				channels == other.channels &&
+				samples_per_second == other.samples_per_second &&
+				average_bytes_per_second == other.average_bytes_per_second &&
+				bits_per_sample == other.bits_per_sample;
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="WaveFormatEx" /> are equal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="WaveFormatEx" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="WaveFormatEx" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    equal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator == (WaveFormatEx first,
+		                                WaveFormatEx second)
+		{
+			return first.Equals (second);
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="WaveFormatEx" /> differ.
+		/// </summary>
+		/// <param name="first">
+		///    A <see cref="WaveFormatEx" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    A <see cref="WaveFormatEx" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    unequal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator != (WaveFormatEx first,
+		                                WaveFormatEx second)
+		{
+			return !first.Equals (second);
+		}
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/StringList.cs b/lib/TagLib/TagLib/StringList.cs
new file mode 100644
index 0000000..656e4af
--- /dev/null
+++ b/lib/TagLib/TagLib/StringList.cs
@@ -0,0 +1,157 @@
+//
+// StringList.cs: This class extends ListBase<string> for a collection
+// of string objects.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//   Aaron Bockover (abockover novell com)
+//
+// Copyright (C) 2006 Novell, Inc.
+// Copyright (C) 2005-2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace TagLib {
+	/// <summary>
+	///    This class extends <see cref="T:TagLib.ListBase`1" /> for a collection of
+	///    <see cref="string" /> objects.
+	/// </summary>
+	[ComVisible(false)]
+	public class StringCollection : ListBase<string>
+	{
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StringCollection" /> with no contents.
+		/// </summary>
+		public StringCollection ()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StringCollection" /> with the contents of another
+		///    instance.
+		/// </summary>
+		/// <param name="values">
+		///    A <see cref="StringCollection" /> object whose values are
+		///    to be added to the new instance.
+		/// </param>
+		public StringCollection (StringCollection values)
+		{
+			Add (values);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StringCollection" /> with the contents of a
+		///    specified array.
+		/// </summary>
+		/// <param name="values">
+		///    A <see cref="string[]" /> whose values are to be added to
+		///    the new instance.
+		/// </param>
+		public StringCollection (params string [] values)
+		{
+			Add (values);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StringCollection" /> by converting a collection of
+		///    <see cref="ByteVector" /> objects to strings with a
+		///    specified encoding.
+		/// </summary>
+		/// <param name="vectorList">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    values to convert and add to the new instance.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="StringType" /> specifying what encoding to
+		///    use when converting the data to strings.
+		/// </param>
+		public StringCollection (ByteVectorCollection vectorList,
+		                         StringType type)
+		{
+			foreach (ByteVector vector in vectorList)
+				Add (vector.ToString (type));
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StringCollection" /> by converting a collection of
+		///    <see cref="ByteVector" /> objects to strings using the
+		///    UTF-8 encoding.
+		/// </summary>
+		/// <param name="vectorList">
+		///    A <see cref="ByteVectorCollection" /> object containing
+		///    values to convert and add to the new instance.
+		/// </param>
+		public StringCollection(ByteVectorCollection vectorList)
+			: this (vectorList, StringType.UTF8)
+		{
+		}
+		
+		/// <summary>
+		///    Splits a single <see cref="string" /> into a <see
+		///    cref="StringCollection" /> using a pattern.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string" /> object to split.
+		/// </param>
+		/// <param name="pattern">
+		///    A <see cref="string" /> object containing a pattern to
+		///    use to split <paramref name="value" />.
+		/// </param>
+		/// <returns>
+		///    A <see cref="StringCollection" /> object containing the
+		///    split values.
+		/// </returns>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="value" /> or <paramref name="pattern" />
+		///    is <see langword="null" />.
+		/// </exception>
+		public static StringCollection Split (string value,
+		                                      string pattern)
+		{
+			if (value == null)
+				throw new ArgumentNullException ("value");
+			
+			if (pattern == null)
+				throw new ArgumentNullException ("pattern");
+			
+			StringCollection list = new StringCollection ();
+			
+			int previous_position = 0;
+			int position = value.IndexOf (pattern, 0);
+			int pattern_length = pattern.Length;
+			
+			while (position != -1) {
+				list.Add (value.Substring (previous_position,
+					position - previous_position));
+				previous_position = position + pattern_length;
+				position = value.IndexOf (pattern,
+					previous_position);
+			}
+			
+			list.Add (value.Substring (previous_position));
+			
+			return list;
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/SupportedMimeType.cs b/lib/TagLib/TagLib/SupportedMimeType.cs
new file mode 100644
index 0000000..51cfe0e
--- /dev/null
+++ b/lib/TagLib/TagLib/SupportedMimeType.cs
@@ -0,0 +1,205 @@
+//
+// SupportedMimeType.cs:
+//
+// Author:
+//   Aaron Bockover (abockover novell com)
+//
+// Original Source:
+//   Entagged#
+//
+// Copyright (C) 2006 Novell, Inc.
+// Copyright (C) 2007 Brian Nickel
+// 
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib {
+	/// <summary>
+	///    This class provides an attribute for listing supported mime-types
+	///    for classes that extend <see cref="File" />.
+	/// </summary>
+	/// <remarks>
+	///    When classes that extend <see cref="File" /> are registered with
+	///    <see cref="FileTypes.Register" />, its <see
+	///    cref="SupportedMimeType" /> attributes are read.
+	/// </remarks>
+	/// <example>
+	///    <code lang="C#">using TagLib;
+	///
+	///[SupportedMimeType("taglib/wv", "wv")]
+	///[SupportedMimeType("audio/x-wavpack")]
+	///public class MyFile : File {
+	///	...
+	///}</code>
+	/// </example>
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
+	public sealed class SupportedMimeType : Attribute
+	{
+		/// <summary>
+		///    Contains the registered <see cref="SupportedMimeType" />
+		///    objects.
+		/// </summary>
+		private static List<SupportedMimeType> mimetypes =
+			new List<SupportedMimeType> ();
+		
+		/// <summary>
+		///    Contains the mime-type.
+		/// </summary>
+		private string mimetype;
+		
+		/// <summary>
+		///    Contains the extension.
+		/// </summary>
+		private string extension;
+		
+		/// <summary>
+		///    Constructs and initializes the <see
+		///    cref="SupportedMimeType" /> class by initializing the
+		///    <see cref="FileTypes" /> class.
+		/// </summary>
+		static SupportedMimeType ()
+		{
+			FileTypes.Init ();
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of the <see
+		///    cref="SupportedMimeType" /> attribute for a specified
+		///    mime-type.
+		/// </summary>
+		/// <param name="mimetype">
+		///    A <see cref="string" /> object containing a standard
+		///    mime-type.
+		/// </param>
+		/// <remarks>
+		///    <para>Standard practice is to use <see
+		///    cref="SupportedMimeType(string)" /> to register standard
+		///    mime-types, like "audio/mp3" and "video/mpeg" and to use
+		///    <see cref="SupportedMimeType(string,string)" /> strictly
+		///    to register extensions, using "taglib/ext" for the mime
+		///    type. Eg. <c>SupportedMimeType("taglib/mp3",
+		///    "mp3")</c>.</para>
+		/// </remarks>
+		public SupportedMimeType (string mimetype)
+		{
+			this.mimetype = mimetype;
+			mimetypes.Add (this);
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of the <see
+		///    cref="SupportedMimeType" /> attribute for a specified
+		///    mime-type and extension.
+		/// </summary>
+		/// <param name="mimetype">
+		///    A <see cref="string" /> object containing a standard
+		///    mime-type.
+		/// </param>
+		/// <param name="extension">
+		///    A <see cref="string" /> object containing a file
+		///    extension.
+		/// </param>
+		/// <remarks>
+		///    <para>Standard practice is to use <see
+		///    cref="SupportedMimeType(string)" /> to register standard
+		///    mime-types, like "audio/mp3" and "video/mpeg" and to use
+		///    <see cref="SupportedMimeType(string,string)" /> strictly
+		///    to register extensions, using "taglib/ext" for the mime
+		///    type. Eg. <c>SupportedMimeType("taglib/mp3",
+		///    "mp3")</c>.</para>
+		/// </remarks>
+		public SupportedMimeType (string mimetype, string extension)
+			: this (mimetype)
+		{
+			this.extension = extension;
+		}
+		
+		/// <summary>
+		///    Gets the mime-type registered by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the mime-type
+		///    registered by the current instance.
+		/// </value>
+		/// <remarks>
+		///    <para>The value is in the format "generic/specific". For
+		///    example, "video/mp4".</para>
+		/// </remarks>
+		public string MimeType {
+			get {return mimetype;}
+		}
+		
+		/// <summary>
+		///    Gets the extension registered by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the extension
+		///    registered by the current instance, or <see
+		///    langword="null" /> if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>The value is the file extension minus the preceding
+		///    ".". For example, "m4v".</para>
+		/// </remarks>
+		public string Extension {
+			get {return extension;}
+		}
+		
+		/// <summary>
+		///    Gets all the mime-types that have been registered with
+		///    <see cref="SupportedMimeType" />.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object containing all the
+		///    mime-types that have been registered with <see
+		///    cref="SupportedMimeType" />.
+		/// </value>
+		/// <remarks>
+		///    <para>These values are used by <see
+		///    cref="TagLib.File.Create(string,string,ReadStyle)" /> to
+		///    match file types.</para>
+		/// </remarks>
+		public static IEnumerable<string> AllMimeTypes {
+			get {
+				foreach(SupportedMimeType type in mimetypes)
+					yield return type.MimeType;
+			}
+		}
+		
+		/// <summary>
+		///    Gets all the extensions that have been registered with
+		///    <see cref="SupportedMimeType" />.
+		/// </summary>
+		/// <value>
+		///    A <see cref="T:System.Collections.Generic.IEnumerable`1" /> object containing all the
+		///    extensions that have been registered with <see
+		///    cref="SupportedMimeType" />.
+		/// </value>
+		/// <remarks>
+		///    <para>These values are currently not used in file type
+		///    recognition.</para>
+		/// </remarks>
+		public static IEnumerable<string> AllExtensions {
+			get {
+				foreach(SupportedMimeType type in mimetypes)
+					if(type.Extension != null)
+						yield return type.Extension;
+			}
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Tag.cs b/lib/TagLib/TagLib/Tag.cs
new file mode 100644
index 0000000..e6a2ce7
--- /dev/null
+++ b/lib/TagLib/TagLib/Tag.cs
@@ -0,0 +1,1327 @@
+//
+// Tag.cs: This abstract class provides generic access to standard tag
+// features. All tag types will extend this class.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   tag.cpp from TagLib
+//
+// Copyright (C) 2005-2007 Brian Nickel
+// Copyright (C) 2003 Scott Wheeler
+// 
+// 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
+//
+
+using System;
+
+namespace TagLib {
+	/// <summary>
+	///    Indicates the tag types used by a file.
+	/// </summary>
+	[Flags]
+	public enum TagTypes : uint
+	{
+		/// <summary>
+		///    No tag types.
+		/// </summary>
+		None         = 0x00000000,
+		
+		/// <summary>
+		///    Xiph's Vorbis Comment
+		/// </summary>
+		Xiph         = 0x00000001,
+		
+		/// <summary>
+		///    ID3v1 Tag
+		/// </summary>
+		Id3v1        = 0x00000002,
+		
+		/// <summary>
+		///    ID3v2 Tag
+		/// </summary>
+		Id3v2        = 0x00000004,
+		
+		/// <summary>
+		///    APE Tag
+		/// </summary>
+		Ape          = 0x00000008,
+		
+		/// <summary>
+		///    Apple's ILST Tag Format
+		/// </summary>
+		Apple        = 0x00000010,
+		
+		/// <summary>
+		///    ASF Tag
+		/// </summary>
+		Asf          = 0x00000020,
+		
+		/// <summary>
+		///    Standard RIFF INFO List Tag
+		/// </summary>
+		RiffInfo     = 0x00000040,
+		
+		/// <summary>
+		///    RIFF Movie ID List Tag
+		/// </summary>
+		MovieId      = 0x00000080,
+		
+		/// <summary>
+		///    DivX Tag
+		/// </summary>
+		DivX         = 0x00000100,
+		
+		/// <summary>
+		///    FLAC Metadata Blocks Tag
+		/// </summary>
+		FlacMetadata = 0x00000200,
+		
+		/// <summary>
+		///    TIFF IFD Tag
+		/// </summary>
+		TiffIFD = 0x00000400,
+		
+		/// <summary>
+		///    XMP Tag
+		/// </summary>
+		XMP = 0x00000800,
+		
+		/// <summary>
+		///    Jpeg Comment Tag
+		/// </summary>
+		JpegComment = 0x00001000,
+		
+		/// <summary>
+		///    All tag types.
+		/// </summary>
+		AllTags      = 0xFFFFFFFF
+	}
+	
+	/// <summary>
+	///    This abstract class provides generic access to standard tag
+	///    features. All tag types will extend this class.
+	/// </summary>
+	/// <remarks>
+	///    Because not every tag type supports the same features, it may be
+	///    useful to check that the value is stored by re-reading the
+	///    property after it is stored.
+	/// </remarks>
+	public abstract class Tag
+	{
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    A bitwise combined <see cref="TagLib.TagTypes" />
+		///    containing the tag types contained in the current
+		///    instance.
+		/// </value>
+		/// <remarks>
+		///    For a standard tag, the value should be intuitive. For
+		///    example, <see cref="TagLib.Id3v2.Tag" /> objects have a
+		///    value of <see cref="TagLib.TagTypes.Id3v2" />. However,
+		///    for tags of type <see cref="TagLib.CombinedTag" /> may
+		///    contain multiple or no types.
+		/// </remarks>
+		public abstract TagTypes TagTypes {get;}
+		
+		/// <summary>
+		///    Gets and sets the title for the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the title for
+		///    the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    The title is most commonly the name of the song or
+		///    episode or a movie title. For example, "Daydream
+		///    Believer" (a song by the Monkies), "Space Seed" (an
+		///    episode of Star Trek), or "Harold and Kumar Go To White
+		///    Castle" (a movie).
+		/// </remarks>
+		public virtual string Title {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort name for the title of the media 
+		///    described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the sort name for
+		///    the title of the media described by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    Possibly used to sort compilations, or episodic content.
+		/// </remarks>
+		public virtual string TitleSort {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the performers or artists who performed in
+		///    the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the performers or
+		///    artists who performed in the media described by the
+		///    current instance or an empty array if no value is
+		///    present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field is most commonly called "Artists" in
+		///    media applications and should be used to represent each
+		///    of the artists appearing in the media. It can be simple
+		///    in theform of "The Beatles", or more complicated in the
+		///    form of "John Lennon, Paul McCartney, George Harrison,
+		///    Pete Best", depending on the preferences of the listener
+		///    and the degree to which they organize their media
+		///    collection.</para>
+		///    <para>As the preference of the user may vary,
+		///    applications should not try to limit the user in what
+		///    choice they may make.</para>
+		/// </remarks>
+		public virtual string [] Performers {
+			get {return new string [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names of the performers or artists
+		///    who performed in the media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names for
+		///    the performers or artists who performed in the media
+		///    described by the current instance, or an empty array if
+		///    no value is present. 
+		/// </value>
+		/// <remarks>
+		///    <para>This is used to provide more control over how tracks
+		///    are sorted. Typical uses are to skip common prefixes or
+		///    sort by last name. For example, "The Beatles" might be
+		///    sorted as "Beatles, The".
+		///    </para>
+		/// </remarks>
+		public virtual string [] PerformersSort {
+			get {return new string [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the band or artist who is credited in the
+		///    creation of the entire album or collection containing the
+		///    media described by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the band or artist
+		///    who is credited in the creation of the entire album or
+		///    collection containing the media described by the current
+		///    instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field is typically optional but aids in the
+		///    sorting of compilations or albums with multiple artists.
+		///    For example, if an album has several artists, sorting by
+		///    artist will split up the album and sorting by album will
+		///    split up albums by the same artist. Having a single album
+		///    artist for an entire album will solve this
+		///    problem.</para>
+		///    <para>As this value is to be used as a sorting key, it
+		///    should be used with less variation than <see
+		///    cref="Performers" />. Where performers can be broken into
+		///    muliple artist it is best to stick with a single band
+		///    name. For example, "The Beatles".</para>
+		/// </remarks>
+		public virtual string [] AlbumArtists {
+			get {return new string [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the band or artist who
+		///    is credited in the creation of the entire album or
+		///    collection containing the media described by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the band or artist who is credited in the creation
+		///    of the entire album or collection containing the media
+		///    described by the current instance or an empty array if
+		///    no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field is typically optional but aids in the
+		///    sorting of compilations or albums with multiple artists.
+		///    For example, if an album has several artists, sorting by
+		///    artist will split up the album and sorting by album will
+		///    split up albums by the same artist. Having a single album
+		///    artist for an entire album will solve this
+		///    problem.</para>
+		///    <para>As this value is to be used as a sorting key, it
+		///    should be used with less variation than <see
+		///    cref="Performers" />. Where performers can be broken into
+		///    muliple artist it is best to stick with a single band
+		///    name. For example, "Beatles, The".</para>
+		/// </remarks>
+		public virtual string [] AlbumArtistsSort {
+			get {return new string [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the composers of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the composers of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the composers, song writers,
+		///    script writers, or persons who claim authorship of the
+		///    media.</para>
+		/// </remarks>
+		public virtual string [] Composers {
+			get {return new string [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the composers of the 
+		///    media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the composers of the media represented by the 
+		///    current instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field is typically optional but aids in the
+		///    sorting of compilations or albums with multiple Composers.
+		///    </para>
+		///    <para>As this value is to be used as a sorting key, it
+		///    should be used with less variation than <see
+		///    cref="Composers" />. Where performers can be broken into
+		///    muliple artist it is best to stick with a single composer.
+		///    For example, "McCartney, Paul".</para>
+		/// </remarks>
+		public virtual string [] ComposersSort {
+			get {return new string [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the album of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the album of
+		///    the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the name of the album the
+		///    media belongs to. In the case of a boxed set, it should
+		///    be the name of the entire set rather than the individual
+		///    disc.</para>
+		///    <para>For example, "Rubber Soul" (an album by the
+		///    Beatles), "The Sopranos: Complete First Season" (a boxed
+		///    set of TV episodes), or "Back To The Future Trilogy" (a 
+		///    boxed set of movies).</para>
+		/// </remarks>
+		public virtual string Album {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the sort names for the Album Title of the 
+		///    media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the sort names
+		///    for the Album Title of the media represented by the 
+		///    current instance or an empty array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field is typically optional but aids in the
+		///    sorting of compilations or albums with Similar Titles.
+		///    </para>
+		/// </remarks>
+		public virtual string AlbumSort {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets a user comment on the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing user comments
+		///    on the media represented by the current instance or <see
+		///    langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field should be used to store user notes and
+		///    comments. There is no constraint on what text can be
+		///    stored here, but it should not contain program
+		///    information.</para>
+		///    <para>Because this field contains notes that the user
+		///    might think of while listening to the media, it may be
+		///    useful for an application to make this field easily
+		///    accessible, perhaps even including it in the main
+		///    interface.</para>
+		/// </remarks>
+		public virtual string Comment {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the genres of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the genres of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents genres that apply to the song
+		///    or album. This is often used for filtering media.</para>
+		///    <para>A list of common audio genres as popularized by
+		///    ID3v1, are stored in <see cref="Genres.Audio" />.
+		///    Additionally, <see cref="Genres.Video" /> contains video
+		///    genres as used by DivX.</para>
+		/// </remarks>
+		public virtual string [] Genres {
+			get {return new string [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the year that the media represented by the
+		///    current instance was recorded.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the year that the media
+		///    represented by the current instance was created or zero
+		///    if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>Years greater than 9999 cannot be stored by most
+		///    tagging formats and will be cleared if a higher value is
+		///    set.</para>
+		///    <para>Some tagging formats store higher precision dates
+		///    which will be truncated when this property is set. Format
+		///    specific implementations are necessary access the higher
+		///    precision values.</para>
+		/// </remarks>
+		public virtual uint Year {
+			get {return 0;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the position of the media represented by
+		///    the current instance in its containing album.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the position of the
+		///    media represented by the current instance in its
+		///    containing album or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>This value should be the same as is listed on the
+		///    album cover and no more than <see cref="TrackCount"
+		///    /> if <see cref="TrackCount" /> is non-zero.</para>
+		/// </remarks>
+		public virtual uint Track {
+			get {return 0;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of tracks in the album
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of tracks in
+		///    the album containing the media represented by the current
+		///    instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>If non-zero, this value should be at least equal to
+		///    <see cref="Track" />. If <see cref="Track" /> is zero,
+		///    this value should also be zero.</para>
+		/// </remarks>
+		public virtual uint TrackCount {
+			get {return 0;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of the disc containing the media
+		///    represented by the current instance in the boxed set.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of the disc
+		///    containing the media represented by the current instance
+		///    in the boxed set.
+		/// </value>
+		/// <remarks>
+		///    <para>This value should be the same as is number that
+		///    appears on the disc. For example, if the disc is the
+		///    first of three, the value should be <c>1</c>. It should
+		///    be no more than <see cref="DiscCount" /> if <see
+		///    cref="DiscCount" /> is non-zero.</para>
+		/// </remarks>
+		public virtual uint Disc {
+			get {return 0;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of discs in the boxed set
+		///    containing the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of discs in
+		///    the boxed set containing the media represented by the
+		///    current instance or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>If non-zero, this value should be at least equal to
+		///    <see cref="Disc" />. If <see cref="Disc" /> is zero,
+		///    this value should also be zero.</para>
+		/// </remarks>
+		public virtual uint DiscCount {
+			get {return 0;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the lyrics or script of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the lyrics or
+		///    script of the media represented by the current instance
+		///    or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field contains a plain text representation of
+		///    the lyrics or scripts with line breaks and whitespace
+		///    being the only formatting marks.</para>
+		///    <para>Some formats support more advances lyrics, like
+		///    synchronized lyrics, but those must be accessed using
+		///    format specific implementations.</para>
+		/// </remarks>
+		public virtual string Lyrics {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the grouping on the album which the media
+		///    in the current instance belongs to.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the grouping on
+		///    the album which the media in the current instance belongs
+		///    to or <see langword="null" /> if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field contains a non-physical grouping to
+		///    which the track belongs. In classical music, this could
+		///    be a movement. It could also be parts of a series like
+		///    "Introduction", "Closing Remarks", etc.</para>
+		/// </remarks>
+		public virtual string Grouping {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the number of beats per minute in the audio
+		///    of the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="uint" /> containing the number of beats per
+		///    minute in the audio of the media represented by the
+		///    current instance, or zero if not specified.
+		/// </value>
+		/// <remarks>
+		///    <para>This field is useful for DJ's who are trying to
+		///    match songs. It should be calculated from the audio or
+		///    pulled from a database.</para>
+		/// </remarks>
+		public virtual uint BeatsPerMinute {
+			get {return 0;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the conductor or director of the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the conductor
+		///    or director of the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field is most useful for organizing classical
+		///    music and movies.</para>
+		/// </remarks>
+		public virtual string Conductor {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the copyright information for the media
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing the copyright
+		///    information for the media represented by the current
+		///    instance or <see langword="null" /> if no value present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field should be used for storing copyright
+		///    information. It may be useful to show this information
+		///    somewhere in the program while the media is
+		///    playing.</para>
+		///    <para>Players should not support editing this field, but
+		///    media creation tools should definitely allow
+		///    modification.</para>
+		/// </remarks>
+		public virtual string Copyright {
+			get {return null;}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the MusicBrainz Artist ID of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz ArtistID of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz ArtistID, and is used
+		///    to uniquely identify a particular Artist of the track.</para>
+		/// </remarks>
+		public virtual string MusicBrainzArtistId {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release ID of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz ReleaseID of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz ReleaseID, and is used
+		///    to uniquely identify a particular Release to which this track belongs.</para>
+		/// </remarks>
+		public virtual string MusicBrainzReleaseId {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Artist ID of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz ReleaseArtistID of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz Release ArtistID, and is used
+		///    to uniquely identify a particular Album Artist credited with the Album.</para>
+		/// </remarks>
+		public virtual string MusicBrainzReleaseArtistId {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Track ID of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz TrackID of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz TrackID, and is used
+		///    to uniquely identify a particular track.</para>
+		/// </remarks>
+		public virtual string MusicBrainzTrackId {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Disc ID of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz DiscID of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz DiscID, and is used
+		///    to uniquely identify the particular Released Media associated with
+		///    this track.</para>
+		/// </remarks>
+		public virtual string MusicBrainzDiscId {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicIP PUID of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicIP PUID of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicIP PUID, and is an acoustic
+		///    fingerprint identifier.  It Identifies what this track "Sounds Like".</para>
+		/// </remarks>
+		public virtual string MusicIpId {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the Amazon ID of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the AmazonID of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the AmazonID, and is used
+		///    to identify the particular track or album in the Amazon Catalog.</para>
+		/// </remarks>
+		public virtual string AmazonId {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Status of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz ReleaseStatus of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz ReleaseStatus, and is used
+		///    to describes how 'official' a Release is.  Common Status are: Official, Promotion,
+		///    Bootleg, Pseudo-release.</para>
+		/// </remarks>
+		public virtual string MusicBrainzReleaseStatus {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Type of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz ReleaseType of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz ReleaseType, that describes
+		///    what kind of release a Release is..  Common Status are: Single, Album,
+		///    EP, Compilation, Soundtrack, SpokenWord, Interview, Audiobook, Live, Remix,
+		///    and Other.  Careful thought must be given when using this field to decide if
+		///    a particular track "Is a Compilation".</para>
+		/// </remarks>
+		public virtual string MusicBrainzReleaseType {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets the MusicBrainz Release Country of the media represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the MusicBrainz ReleaseCountry of the
+		///    media represented by the current instance or an empty
+		///    array if no value is present.
+		/// </value>
+		/// <remarks>
+		///    <para>This field represents the MusicBrainz ReleaseCountry, that describes
+		///    the country in which an album was released.  Note that the ReleaseCountry 
+		///    of an album is not necessarily the country in which it was produced. The 
+		///    label itself will typically be more relevant. eg, a release on "Foo Records UK" 
+		///    that has "Made in Austria" printed on it, will likely be a UK release.</para>
+		/// </remarks>
+		public virtual string MusicBrainzReleaseCountry {
+			get { return null; }
+			set {}
+		}
+
+		/// <summary>
+		///    Gets and sets a collection of pictures associated with
+		///    the media represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="IPicture[]" /> containing a collection of
+		///    pictures associated with the media represented by the
+		///    current instance or an empty array if none are present.
+		/// </value>
+		/// <remarks>
+		///    <para>Typically, this value is used to store an album
+		///    cover or icon to use for the file, but it is capable of
+		///    holding any type of image, including pictures of the
+		///    band, the recording studio, the concert, etc.</para>
+		/// </remarks>
+		public virtual IPicture [] Pictures {
+			get {return new Picture [] {};}
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets and sets the same value as <see cref="Performers"
+		///    />.
+		/// </summary>
+		/// <value>
+		///    The same value as <see cref="Performers" />.
+		/// </value>
+		/// <remarks>
+		///    This property exists to aleviate confusion. Use <see
+		///    cref="Performers" /> for track artists and <see
+		///    cref="AlbumArtists" /> for album artists.
+		/// </remarks>
+		[Obsolete("For album artists use AlbumArtists. For track artists, use Performers")]
+		public virtual string [] Artists {
+			get {return Performers;}
+			set {Performers = value;}
+		}
+		
+		/// <summary>
+		///    Gets the same value as <see cref="FirstPerformer" />.
+		/// </summary>
+		/// <value>
+		///    The same value as <see cref="FirstPerformer" />.
+		/// </value>
+		/// <remarks>
+		///    This property exists to aleviate confusion. Use <see
+		///    cref="FirstPerformer" /> for track artists and <see
+		///    cref="FirstAlbumArtist" /> for album artists.
+		/// </remarks>
+		[Obsolete("For album artists use FirstAlbumArtist. For track artists, use FirstPerformer")]
+		public string FirstArtist {
+			get {return FirstPerformer;}
+		}
+		
+		/// <summary>
+		///    Gets the first value contained in <see
+		///    cref="AlbumArtists" />.
+		/// </summary>
+		/// <value>
+		///    The first <see cref="string" /> object in <see
+		///    cref="AlbumArtists" />, or <see langword="null" /> is it
+		///    contains no values.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="AlbumArtists" /> to set the value.
+		/// </remarks>
+		public string FirstAlbumArtist {
+			get {return FirstInGroup(AlbumArtists);}
+		}
+		
+		/// <summary>
+		///    Gets the first value contained in <see
+		///    cref="AlbumArtistsSort" />.
+		/// </summary>
+		/// <value>
+		///    The first <see cref="string" /> object in <see
+		///    cref="AlbumArtistsSort" />, or <see langword="null" /> is it
+		///    contains no values.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="AlbumArtistsSort" /> to set the value.
+		/// </remarks>
+		public string FirstAlbumArtistSort {
+			get {return FirstInGroup(AlbumArtistsSort);}
+		}
+		
+		/// <summary>
+		///    Gets the first value contained in <see
+		///    cref="Performers" />.
+		/// </summary>
+		/// <value>
+		///    The first <see cref="string" /> object in <see
+		///    cref="Performers" />, or <see langword="null" /> is it
+		///    contains no values.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="Performers" /> to set the value.
+		/// </remarks>
+		public string FirstPerformer {
+			get {return FirstInGroup(Performers);}
+		}
+
+		/// <summary>
+		///    Gets the first value contained in <see
+		///    cref="PerformersSort" />.
+		/// </summary>
+		/// <value>
+		///    The first <see cref="string" /> object in <see
+		///    cref="PerformersSort" />, or <see langword="null" /> is it
+		///    contains no values.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="PerformersSort" /> to set the value.
+		/// </remarks>
+		public string FirstPerformerSort {
+			get {return FirstInGroup(PerformersSort);}
+		}
+		
+		/// <summary>
+		///    Gets the first value contained in <see
+		///    cref="ComposersSort" />.
+		/// </summary>
+		/// <value>
+		///    The first <see cref="string" /> object in <see
+		///    cref="ComposersSort" />, or <see langword="null" /> is it
+		///    contains no values.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="ComposersSort" /> to set the value.
+		/// </remarks>
+		public string FirstComposerSort {
+			get {return FirstInGroup(ComposersSort);}
+		}
+		
+		/// <summary>
+		///    Gets the first value contained in <see
+		///    cref="Composers" />.
+		/// </summary>
+		/// <value>
+		///    The first <see cref="string" /> object in <see
+		///    cref="Composers" />, or <see langword="null" /> is it
+		///    contains no values.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="Composers" /> to set the value.
+		/// </remarks>
+		public string FirstComposer {
+			get {return FirstInGroup(Composers);}
+		}
+		
+		/// <summary>
+		///    Gets the first value contained in <see cref="Genres" />.
+		/// </summary>
+		/// <value>
+		///    The first <see cref="string" /> object in <see
+		///    cref="Genres" />, or <see langword="null" /> is it
+		///    contains no values.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="Genres" /> to set the value.
+		/// </remarks>
+		public string FirstGenre {
+			get {return FirstInGroup(Genres);}
+		}
+		
+		/// <summary>
+		///    Gets the same value as <see cref="JoinedPerformers" />.
+		/// </summary>
+		/// <value>
+		///    The same value as <see cref="JoinedPerformers" />.
+		/// </value>
+		/// <remarks>
+		///    This property exists to aleviate confusion. Use <see
+		///    cref="JoinedPerformers" /> for track artists and <see
+		///    cref="JoinedAlbumArtists" /> for album artists.
+		/// </remarks>
+		[Obsolete("For album artists use JoinedAlbumArtists. For track artists, use JoinedPerformers")]
+		public string JoinedArtists {
+			get {return JoinedPerformers;}
+		}
+		
+		/// <summary>
+		///    Gets a semicolon separated string containing the values
+		///    in <see cref="AlbumArtists" />.
+		/// </summary>
+		/// <value>
+		///    A semicolon separated <see cref="string" /> object
+		///    containing the values in <see cref="AlbumArtists" />.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="AlbumArtists" /> to set the value.
+		/// </remarks>
+		public string JoinedAlbumArtists {
+			get {return JoinGroup(AlbumArtists);}
+		}
+		
+		/// <summary>
+		///    Gets a semicolon separated string containing the values
+		///    in <see cref="Performers" />.
+		/// </summary>
+		/// <value>
+		///    A semicolon separated <see cref="string" /> object
+		///    containing the values in <see cref="Performers" />.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="Performers" /> to set the value.
+		/// </remarks>
+		public string JoinedPerformers {
+			get {return JoinGroup(Performers);}
+		}
+		
+		/// <summary>
+		///    Gets a semicolon separated string containing the values
+		///    in <see cref="PerformersSort" />.
+		/// </summary>
+		/// <value>
+		///    A semicolon separated <see cref="string" /> object
+		///    containing the values in <see cref="PerformersSort" />.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="PerformersSort" /> to set the value.
+		/// </remarks>
+		public string JoinedPerformersSort {
+			get {return JoinGroup(PerformersSort);}
+		}
+		
+		/// <summary>
+		///    Gets a semicolon separated string containing the values
+		///    in <see cref="Composers" />.
+		/// </summary>
+		/// <value>
+		///    A semicolon separated <see cref="string" /> object
+		///    containing the values in <see cref="Composers" />.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="Composers" /> to set the value.
+		/// </remarks>
+		public string JoinedComposers {
+			get {return JoinGroup(Composers);}
+		}
+		
+		/// <summary>
+		///    Gets a semicolon separated string containing the values
+		///    in <see cref="Genres" />.
+		/// </summary>
+		/// <value>
+		///    A semicolon separated <see cref="string" /> object
+		///    containing the values in <see cref="Genres" />.
+		/// </value>
+		/// <remarks>
+		///    This property is provided for convenience. Use <see
+		///    cref="Genres" /> to set the value.
+		/// </remarks>
+		public string JoinedGenres {
+			get {return JoinGroup(Genres);}
+		}
+		
+		/// <summary>
+		///    Gets the first string in an array.
+		/// </summary>
+		/// <param name="group">
+		///    A <see cref="string[]" /> to get the first string from.
+		/// </param>
+		/// <returns>
+		///    The first <see cref="string" /> object contained in
+		///    <paramref name="group" />, or <see langword="null" /> if
+		///    the array is <see langword="null" /> or empty.
+		/// </returns>
+		private static string FirstInGroup(string [] group)
+		{
+			return group == null || group.Length == 0 ?
+				null : group [0];
+		}
+		
+		/// <summary>
+		///    Joins a array of strings into a single, semicolon
+		///    separated, string.
+		/// </summary>
+		/// <param name="group">
+		///    A <see cref="string[]" /> containing values to combine.
+		/// </param>
+		/// <returns>
+		///    A semicolon separated <see cref="string" /> object
+		///    containing the values from <paramref name="group" />.
+		/// </returns>
+		private static string JoinGroup (string [] group)
+		{
+			if (group == null)
+				return null;
+			
+			return string.Join ("; ", group);
+		}
+
+		/// <summary>
+		///    Gets whether or not the current instance is empty.
+		/// </summary>
+		/// <value>
+		///    <see langword="true" /> if the current instance does not
+		///    any values. Otherwise <see langword="false" />.
+		/// </value>
+		/// <remarks>
+		///    In the default implementation, this checks the values
+		///    supported by <see cref="Tag" />, but it may be extended
+		///    by child classes to support other values.
+		/// </remarks>
+		public virtual bool IsEmpty {
+			get {
+				return IsNullOrLikeEmpty (Title) &&
+				IsNullOrLikeEmpty (Grouping) &&
+				IsNullOrLikeEmpty (AlbumArtists) &&
+				IsNullOrLikeEmpty (Performers) &&
+				IsNullOrLikeEmpty (Composers) &&
+				IsNullOrLikeEmpty (Conductor) &&
+				IsNullOrLikeEmpty (Copyright) &&
+				IsNullOrLikeEmpty (Album) &&
+				IsNullOrLikeEmpty (Comment) &&
+				IsNullOrLikeEmpty (Genres) &&
+				Year == 0 &&
+				BeatsPerMinute == 0 &&
+				Track == 0 &&
+				TrackCount == 0 &&
+				Disc == 0 &&
+				DiscCount == 0;
+			}
+		}
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		/// <remarks>
+		///    The clearing procedure is format specific and should
+		///    clear all values.
+		/// </remarks>
+		public abstract void Clear ();
+		
+		/// <summary>
+		///    Copies all standard values from one tag to another,
+		///    optionally overwriting existing values.
+		/// </summary>
+		/// <param name="source">
+		///    A <see cref="Tag" /> object containing the source tag to
+		///    copy the values from.
+		/// </param>
+		/// <param name="target">
+		///    A <see cref="Tag" /> object containing the target tag to
+		///    copy values to.
+		/// </param>
+		/// <param name="overwrite">
+		///    A <see cref="bool" /> specifying whether or not to copy
+		///    values over existing one.
+		/// </param>
+		/// <remarks>
+		///    <para>This method only copies the most basic values,
+		///    those contained in this class, between tags. To copy
+		///    format specific tags, or additional details, additional
+		///    implementations need to be applied. For example, copying
+		///    from one <see cref="TagLib.Id3v2.Tag" /> to another:
+		///    <c>foreach (TagLib.Id3v2.Frame frame in old_tag)
+		///    new_tag.AddFrame (frame);</c></para>
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="source" /> or <paramref name="target" />
+		///    is <see langword="null" />.
+		/// </exception>
+		[Obsolete("Use Tag.CopyTo(Tag,bool)")]
+		public static void Duplicate (Tag source, Tag target,
+		                              bool overwrite)
+		{
+			if (source == null)
+				throw new ArgumentNullException ("source");
+			
+			if (target == null)
+				throw new ArgumentNullException ("target");
+			
+			source.CopyTo (target, overwrite);
+		}
+		
+		/// <summary>
+		///    Copies the values from the current instance to another
+		///    <see cref="TagLib.Tag" />, optionally overwriting
+		///    existing values.
+		/// </summary>
+		/// <param name="target">
+		///    A <see cref="Tag" /> object containing the target tag to
+		///    copy values to.
+		/// </param>
+		/// <param name="overwrite">
+		///    A <see cref="bool" /> specifying whether or not to copy
+		///    values over existing one.
+		/// </param>
+		/// <remarks>
+		///    <para>This method only copies the most basic values when
+		///    copying between different tag formats, however, if
+		///    <paramref name="target" /> is of the same type as the
+		///    current instance, more advanced copying may be done.
+		///    For example, <see cref="TagLib.Id3v2.Tag" /> will copy
+		///    all of its frames to another tag.</para>
+		/// </remarks>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="target" /> is <see langword="null" />.
+		/// </exception>
+		public virtual void CopyTo (Tag target, bool overwrite)
+		{
+			if (target == null)
+				throw new ArgumentNullException ("target");
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Title))
+				target.Title = Title;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.AlbumArtists))
+				target.AlbumArtists = AlbumArtists;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Performers))
+				target.Performers = Performers;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Composers))
+				target.Composers = Composers;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Album))
+				target.Album = Album;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Comment))
+				target.Comment = Comment;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Genres))
+				target.Genres = Genres;
+			
+			if (overwrite || target.Year == 0)
+				target.Year = Year;
+			
+			if (overwrite || target.Track == 0)
+				target.Track = Track;
+			
+			if (overwrite || target.TrackCount == 0)
+				target.TrackCount = TrackCount;
+			
+			if (overwrite || target.Disc == 0)
+				target.Disc = Disc;
+			
+			if (overwrite || target.DiscCount == 0)
+				target.DiscCount = DiscCount;
+			
+			if (overwrite || target.BeatsPerMinute == 0)
+				target.BeatsPerMinute = BeatsPerMinute;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Grouping))
+				target.Grouping = Grouping;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Conductor))
+				target.Conductor = Conductor;
+			
+			if (overwrite || IsNullOrLikeEmpty (target.Copyright))
+				target.Copyright = Copyright;
+		}
+		
+		/// <summary>
+		///    Checks if a <see cref="string" /> is <see langword="null"
+		///    /> or contains only whitespace characters.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string" /> object to check.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the string is <see
+		///    langword="null" /> or contains only whitespace
+		///    characters. Otherwise <see langword="false" />.
+		/// </returns>
+		private static bool IsNullOrLikeEmpty (string value)
+		{
+			return value == null || value.Trim ().Length == 0;
+		}
+		
+		/// <summary>
+		///    Checks if all the strings in the array return <see
+		///    langword="true" /> with <see
+		///    cref="IsNullOrLikeEmpty(string)" /> or if the array is
+		///    <see langword="null" /> or is empty.
+		/// </summary>
+		/// <param name="value">
+		///    A <see cref="string[]" /> to check the contents of.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if the array is <see
+		///    langword="null" /> or empty, or all elements return <see
+		///    langword="true" /> for <see
+		///    cref="IsNullOrLikeEmpty(string)" />. Otherwise <see
+		///    langword="false" />.
+		/// </returns>
+		private static bool IsNullOrLikeEmpty (string [] value)
+		{
+			if (value == null)
+				return true;
+			
+			foreach (string s in value)
+				if (!IsNullOrLikeEmpty (s))
+					return false;
+			
+			return true;
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/TagLib.sources b/lib/TagLib/TagLib/TagLib.sources
new file mode 100644
index 0000000..b5b80d3
--- /dev/null
+++ b/lib/TagLib/TagLib/TagLib.sources
@@ -0,0 +1,189 @@
+TAGLIB_CSFILES = \
+	$(srcdir)/TagLib/Aac/AudioHeader.cs \
+	$(srcdir)/TagLib/Aac/BitStream.cs \
+	$(srcdir)/TagLib/Aac/File.cs \
+	$(srcdir)/TagLib/Aiff/File.cs \
+	$(srcdir)/TagLib/Aiff/StreamHeader.cs \
+	$(srcdir)/TagLib/Ape/Tag.cs \
+	$(srcdir)/TagLib/Ape/Item.cs \
+	$(srcdir)/TagLib/Ape/Footer.cs \
+	$(srcdir)/TagLib/Ape/File.cs \
+	$(srcdir)/TagLib/Ape/StreamHeader.cs \
+	$(srcdir)/TagLib/Asf/Guid.cs \
+	$(srcdir)/TagLib/Asf/StreamPropertiesObject.cs \
+	$(srcdir)/TagLib/Asf/UnknownObject.cs \
+	$(srcdir)/TagLib/Asf/Tag.cs \
+	$(srcdir)/TagLib/Asf/Object.cs \
+	$(srcdir)/TagLib/Asf/ExtendedContentDescriptionObject.cs \
+	$(srcdir)/TagLib/Asf/ContentDescriptionObject.cs \
+	$(srcdir)/TagLib/Asf/MetadataLibraryObject.cs \
+	$(srcdir)/TagLib/Asf/HeaderObject.cs \
+	$(srcdir)/TagLib/Asf/HeaderExtensionObject.cs \
+	$(srcdir)/TagLib/Asf/File.cs \
+	$(srcdir)/TagLib/Asf/FilePropertiesObject.cs \
+	$(srcdir)/TagLib/Asf/PaddingObject.cs \
+	$(srcdir)/TagLib/Asf/ContentDescriptor.cs \
+	$(srcdir)/TagLib/Asf/DescriptionRecord.cs \
+	$(srcdir)/TagLib/Mpc/StreamHeader.cs \
+	$(srcdir)/TagLib/Mpc/File.cs \
+	$(srcdir)/TagLib/Ogg/GroupedComment.cs \
+	$(srcdir)/TagLib/Ogg/Bitstream.cs \
+	$(srcdir)/TagLib/Ogg/Paginator.cs \
+	$(srcdir)/TagLib/Ogg/Codec.cs \
+	$(srcdir)/TagLib/Ogg/Codecs/Vorbis.cs \
+	$(srcdir)/TagLib/Ogg/Codecs/Theora.cs \
+	$(srcdir)/TagLib/Ogg/Page.cs \
+	$(srcdir)/TagLib/Ogg/XiphComment.cs \
+	$(srcdir)/TagLib/Ogg/PageHeader.cs \
+	$(srcdir)/TagLib/Ogg/File.cs \
+	$(srcdir)/TagLib/Flac/Block.cs \
+	$(srcdir)/TagLib/Flac/BlockHeader.cs \
+	$(srcdir)/TagLib/Flac/StreamHeader.cs \
+	$(srcdir)/TagLib/Flac/Picture.cs \
+	$(srcdir)/TagLib/Flac/File.cs \
+	$(srcdir)/TagLib/Image/Codec.cs \
+	$(srcdir)/TagLib/Image/CombinedImageTag.cs \
+	$(srcdir)/TagLib/Image/File.cs \
+	$(srcdir)/TagLib/Image/ImageTag.cs \
+	$(srcdir)/TagLib/Image/ImageOrientation.cs \
+	$(srcdir)/TagLib/Jpeg/Codec.cs \
+	$(srcdir)/TagLib/Jpeg/File.cs \
+	$(srcdir)/TagLib/Jpeg/JpegCommentTag.cs \
+	$(srcdir)/TagLib/Jpeg/Marker.cs \
+	$(srcdir)/TagLib/Jpeg/Table.cs \
+	$(srcdir)/TagLib/Mpeg/XingHeader.cs \
+	$(srcdir)/TagLib/Mpeg/VBRIHeader.cs \
+	$(srcdir)/TagLib/Mpeg/File.cs \
+	$(srcdir)/TagLib/Mpeg/AudioFile.cs \
+	$(srcdir)/TagLib/Mpeg/AudioHeader.cs \
+	$(srcdir)/TagLib/Mpeg/VideoHeader.cs \
+	$(srcdir)/TagLib/NonContainer/EndTag.cs \
+	$(srcdir)/TagLib/NonContainer/File.cs \
+	$(srcdir)/TagLib/NonContainer/StartTag.cs \
+	$(srcdir)/TagLib/NonContainer/Tag.cs \
+	$(srcdir)/TagLib/Id3v1/Tag.cs \
+	$(srcdir)/TagLib/Id3v1/StringHandler.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/PopularimeterFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/PlayCountFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/PrivateFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/RelativeVolumeFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/UniqueFileIdentifierFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/UnknownFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/CommentsFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/TextIdentificationFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/AttachedPictureFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/GeneralEncapsulatedObjectFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/UnsynchronisedLyricsFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/SynchronizedLyricsFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/MusicCdIdentifierFrame.cs \
+	$(srcdir)/TagLib/Id3v2/Frames/TermsOfUseFrame.cs \
+	$(srcdir)/TagLib/Id3v2/FrameFactory.cs \
+	$(srcdir)/TagLib/Id3v2/Frame.cs \
+	$(srcdir)/TagLib/Id3v2/FrameTypes.cs \
+	$(srcdir)/TagLib/Id3v2/Tag.cs \
+	$(srcdir)/TagLib/Id3v2/FrameHeader.cs \
+	$(srcdir)/TagLib/Id3v2/ExtendedHeader.cs \
+	$(srcdir)/TagLib/Id3v2/SynchData.cs \
+	$(srcdir)/TagLib/Id3v2/Header.cs \
+	$(srcdir)/TagLib/Id3v2/Footer.cs \
+	$(srcdir)/TagLib/IFD/Entries/ByteIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/ByteVectorIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/LongArrayIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/LongIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/MakernoteIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/RationalArrayIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/RationalIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/Rational.cs \
+	$(srcdir)/TagLib/IFD/Entries/SByteIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/ShortArrayIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/ShortIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/SLongArrayIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/SLongIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/SRationalIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/SShortArrayIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/SShortIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/SRational.cs \
+	$(srcdir)/TagLib/IFD/Entries/StringIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/StripOffsetsIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/SubIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/ThumbnailDataIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/UserCommentIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/Entries/UndefinedIFDEntry.cs \
+	$(srcdir)/TagLib/IFD/IFDEntry.cs \
+	$(srcdir)/TagLib/IFD/IFDEntryType.cs \
+	$(srcdir)/TagLib/IFD/IFDTag.cs \
+	$(srcdir)/TagLib/IFD/IFDDirectory.cs \
+	$(srcdir)/TagLib/IFD/IFDStructure.cs \
+	$(srcdir)/TagLib/IFD/IFDReader.cs \
+	$(srcdir)/TagLib/IFD/IFDRenderer.cs \
+	$(srcdir)/TagLib/IFD/Makernotes/Nikon3MakernoteReader.cs \
+	$(srcdir)/TagLib/IFD/Tags/CanonMakerNoteEntryTag.cs \
+	$(srcdir)/TagLib/IFD/Tags/ExifEntryTag.cs \
+	$(srcdir)/TagLib/IFD/Tags/GPSEntryTag.cs \
+	$(srcdir)/TagLib/IFD/Tags/IFDEntryTag.cs \
+	$(srcdir)/TagLib/IFD/Tags/IOPEntryTag.cs \
+	$(srcdir)/TagLib/IFD/Tags/Nikon3MakerNoteEntryTag.cs \
+	$(srcdir)/TagLib/IFD/Tags/NikonPreviewMakerNoteEntryTag.cs \
+	$(srcdir)/TagLib/IFD/Tags/PanasonicMakerNoteEntryTag.cs \
+	$(srcdir)/TagLib/Mpeg4/AppleTag.cs \
+	$(srcdir)/TagLib/Mpeg4/Box.cs \
+	$(srcdir)/TagLib/Mpeg4/BoxFactory.cs \
+	$(srcdir)/TagLib/Mpeg4/BoxHeader.cs \
+	$(srcdir)/TagLib/Mpeg4/BoxTypes.cs \
+	$(srcdir)/TagLib/Mpeg4/File.cs \
+	$(srcdir)/TagLib/Mpeg4/FileParser.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoFreeSpaceBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/UnknownBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoUserDataBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoChunkOffsetBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoChunkLargeOffsetBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/AppleItemListBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/AppleAnnotationBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoSampleTableBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoSampleEntry.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoAudioSampleEntry.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoVisualSampleEntry.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoMetaBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoSampleDescriptionBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/AppleAdditionalInfoBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoHandlerBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/IsoMovieHeaderBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/FullBox.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/AppleElementaryStreamDescriptor.cs \
+	$(srcdir)/TagLib/Mpeg4/Boxes/AppleDataBox.cs \
+	$(srcdir)/TagLib/Riff/AviHeaderList.cs \
+	$(srcdir)/TagLib/Riff/AviStream.cs \
+	$(srcdir)/TagLib/Riff/WaveFormatEx.cs \
+	$(srcdir)/TagLib/Riff/BitmapInfoHeader.cs \
+	$(srcdir)/TagLib/Riff/DivXTag.cs \
+	$(srcdir)/TagLib/Riff/File.cs \
+	$(srcdir)/TagLib/Riff/InfoTag.cs \
+	$(srcdir)/TagLib/Riff/List.cs \
+	$(srcdir)/TagLib/Riff/ListTag.cs \
+	$(srcdir)/TagLib/Riff/MovieIdTag.cs \
+	$(srcdir)/TagLib/Tiff/Codec.cs \
+	$(srcdir)/TagLib/Tiff/File.cs \
+	$(srcdir)/TagLib/WavPack/File.cs \
+	$(srcdir)/TagLib/WavPack/StreamHeader.cs \
+	$(srcdir)/TagLib/Xmp/XmlNodeExtensions.cs \
+	$(srcdir)/TagLib/Xmp/XmpTag.cs \
+	$(srcdir)/TagLib/Xmp/XmpNode.cs \
+	$(srcdir)/TagLib/Xmp/XmpNodeType.cs \
+	$(srcdir)/TagLib/Xmp/XmpNodeVisitor.cs \
+	$(srcdir)/TagLib/ICodec.cs \
+	$(srcdir)/TagLib/Tag.cs \
+	$(srcdir)/TagLib/ReadOnlyByteVector.cs \
+	$(srcdir)/TagLib/ByteVector.cs \
+	$(srcdir)/TagLib/ByteVectorList.cs \
+	$(srcdir)/TagLib/CombinedTag.cs \
+	$(srcdir)/TagLib/Genres.cs \
+	$(srcdir)/TagLib/Properties.cs \
+	$(srcdir)/TagLib/File.cs \
+	$(srcdir)/TagLib/StringList.cs \
+	$(srcdir)/TagLib/SupportedMimeType.cs \
+	$(srcdir)/TagLib/UnsupportedFormatException.cs \
+	$(srcdir)/TagLib/Picture.cs \
+	$(srcdir)/TagLib/ListBase.cs \
+	$(srcdir)/TagLib/FileTypes.cs \
+	$(srcdir)/TagLib/CorruptFileException.cs
+
diff --git a/lib/TagLib/TagLib/Tiff/Codec.cs b/lib/TagLib/TagLib/Tiff/Codec.cs
new file mode 100644
index 0000000..b6b110a
--- /dev/null
+++ b/lib/TagLib/TagLib/Tiff/Codec.cs
@@ -0,0 +1,61 @@
+//
+// Codec.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+
+namespace TagLib.Tiff
+{
+	/// <summary>
+	///    A TIFF photo codec. Contains basic photo details.
+	/// </summary>
+	public class Codec : Image.Codec
+	{
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public override string Description { get { return "TIFF File"; } }
+
+		
+		/// <summary>
+		///    Constructs a new <see cref="Codec" /> with the given width
+		///    and height.
+		/// </summary>
+		/// <param name="width">
+		///    The width of the photo.
+		/// </param>
+		/// <param name="height">
+		///    The height of the photo.
+		/// </param>
+		/// <returns>
+		///    A new <see cref="Codec" /> instance.
+		/// </returns>
+		public Codec (int width, int height)
+			: base (width, height) {}
+	}
+}
diff --git a/lib/TagLib/TagLib/Tiff/File.cs b/lib/TagLib/TagLib/Tiff/File.cs
new file mode 100644
index 0000000..2227013
--- /dev/null
+++ b/lib/TagLib/TagLib/Tiff/File.cs
@@ -0,0 +1,327 @@
+//
+// File.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using TagLib.Image;
+using TagLib.IFD;
+using TagLib.IFD.Entries;
+using TagLib.IFD.Tags;
+using TagLib.Xmp;
+
+namespace TagLib.Tiff
+{
+	/// <summary>
+	///    This class extends <see cref="TagLib.File" /> to provide tagging
+	///    and properties support for Tiff files.
+	/// </summary>
+	[SupportedMimeType("taglib/tiff", "tiff")]
+	[SupportedMimeType("taglib/tif", "tif")]
+	[SupportedMimeType("image/tiff")]
+	public class File : TagLib.Image.File
+	{
+#region Private Fields
+		
+		/// <summary>
+		///    Contains the media properties.
+		/// </summary>
+		private Properties properties;
+
+		/// <summary>
+		///    Whether or not the file is big-endian.
+		/// </summary>
+		private bool is_bigendian;
+		
+#endregion		
+		
+#region Constructors		
+
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: this (new File.LocalFileAbstraction (path),
+				propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle) : base (abstraction)
+		{
+			ImageTag = new CombinedImageTag (TagTypes.TiffIFD | TagTypes.XMP);
+			
+			Mode = AccessMode.Read;
+			try {
+				Read (propertiesStyle);
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		protected File (IFileAbstraction abstraction) : base (abstraction)
+		{
+		}
+		
+#endregion
+		
+#region Public Properties
+		
+		/// <summary>
+		///    Gets the media properties of the file represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Properties" /> object containing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </value>
+		public override TagLib.Properties Properties {
+			get { return properties; }
+		}
+		
+#endregion
+		
+#region Public Methods
+		
+		/// <summary>
+		///    Saves the changes made in the current instance to the
+		///    file it represents.
+		/// </summary>
+		public override void Save ()
+		{
+			Mode = AccessMode.Write;
+			try {
+				WriteFile ();
+				
+				TagTypesOnDisk = TagTypes;
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+
+#endregion
+
+#region Private Methods
+
+		/// <summary>
+		///    Render the whole file and write it back.
+		/// </summary>
+		private void WriteFile ()
+		{
+			// Check, if IFD0 is contained
+			IFDTag exif = ImageTag.Exif;
+			if (exif == null)
+				throw new Exception ("Tiff file without tags");
+			
+			UpdateTags (exif);
+			
+			// first IFD starts at 8
+			uint first_ifd_offset = 8;
+			
+			ByteVector data = new ByteVector ();
+			
+			if (is_bigendian)
+				data.Add ("MM");
+			else
+				data.Add ("II");
+			
+			data.Add (ByteVector.FromUShort (42, is_bigendian));
+			data.Add (ByteVector.FromUInt (first_ifd_offset, is_bigendian));
+			
+			var renderer = new IFDRenderer (is_bigendian, exif.Structure, first_ifd_offset);
+			
+			data.Add (renderer.Render ());
+			
+			Insert (data, 0, Length);
+		}
+		
+		/// <summary>
+		///    Update the XMP stored in the Tiff IFD
+		/// </summary>
+		/// <param name="exif">
+		///    A <see cref="IFDTag"/> The Tiff IFD to update the entries
+		/// </param>
+		private void UpdateTags (IFDTag exif)
+		{
+			// update the XMP entry
+			exif.Structure.RemoveTag (0, (ushort) IFDEntryTag.XMP);
+			
+			XmpTag xmp = ImageTag.Xmp;
+			if (xmp != null)
+				exif.Structure.AddEntry (0, new ByteVectorIFDEntry ((ushort) IFDEntryTag.XMP, xmp.Render ()));
+		}
+		
+		/// <summary>
+		///    Starts parsing the TIFF header of the file from beginning
+		///    and sets <see cref="is_bigendian"/> according to the header.
+		///    The method returns the offset to the first IFD.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.UInt32"/> representing the offset to first
+		///    IFD of this file.
+		/// </returns>
+		private uint ReadFirstIFDOffset ()
+		{
+			// 2 + 2 + 4 (Byte Order + TIFF Magic Number + Offset)
+			int tiff_header_size = 8;
+			
+			Seek (0, SeekOrigin.Begin);
+			
+			ByteVector header = ReadBlock (tiff_header_size);
+			
+			is_bigendian = header.Mid (0, 2).ToString ().Equals ("MM");
+			
+			ushort magic = header.Mid (2, 2).ToUShort (is_bigendian);
+			if (magic != 42)
+				throw new Exception (String.Format ("Invalid TIFF magic: {0}", magic));
+			
+			return header.Mid (4, 4).ToUInt (is_bigendian);		
+		}
+		
+		
+		/// <summary>
+		///    Reads the file with a specified read style.
+		/// </summary>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		private void Read (ReadStyle propertiesStyle)
+		{
+			Mode = AccessMode.Read;
+			try {
+				uint offset = ReadFirstIFDOffset ();
+				
+				var ifd_tag = new IFDTag ();
+				var reader = new IFDReader (this, is_bigendian, ifd_tag.Structure, 0, offset, (uint) Length);
+				reader.Read ();
+				ImageTag.AddTag (ifd_tag);
+
+				// Find XMP data
+				var xmp_entry = ifd_tag.Structure.GetEntry (0, (ushort) IFDEntryTag.XMP) as ByteVectorIFDEntry;
+				if (xmp_entry != null) {
+					ImageTag.AddTag (new XmpTag (xmp_entry.Data.ToString ()));
+				}
+
+				if (propertiesStyle == ReadStyle.None)
+					return;
+
+				properties = ExtractProperties ();
+			} finally {
+				Mode = AccessMode.Closed;
+			}
+		}
+
+		/// <summary>
+		///    Attempts to extract the media properties of the main
+		///    photo.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="Properties" /> object with a best effort guess
+		///    at the right values. When no guess at all can be made,
+		///    <see langword="null" /> is returned.
+		/// </returns>
+		private Properties ExtractProperties ()
+		{
+			int width = 0, height = 0;
+
+			IFDTag tag = GetTag (TagTypes.TiffIFD) as IFDTag;
+			IFDStructure structure = tag.Structure;
+			
+			width = (int) (structure.GetLongValue (0, (ushort) IFDEntryTag.ImageWidth) ?? 0);
+			height = (int) (structure.GetLongValue (0, (ushort) IFDEntryTag.ImageLength) ?? 0);
+
+			if (width > 0 && height > 0) {
+				return new Properties (TimeSpan.Zero, new Codec (width, height));
+			}
+
+			return null;
+		}
+
+#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/UnsupportedFormatException.cs b/lib/TagLib/TagLib/UnsupportedFormatException.cs
new file mode 100644
index 0000000..93fbcd7
--- /dev/null
+++ b/lib/TagLib/TagLib/UnsupportedFormatException.cs
@@ -0,0 +1,164 @@
+//
+// UnsupportedFormatException.cs:
+//
+// Author:
+//   Aaron Bockover (abockover novell com)
+//
+// Original Source:
+//   Entagged#
+//
+// Copyright (C) 2005-2006 Novell, Inc.
+// 
+// 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
+//
+
+using System;
+using System.Runtime.Serialization;
+
+namespace TagLib {
+	/// <summary>
+	///    This class extends <see cref="Exception" /> and is used to
+	///    indicate that a file or tag is stored in an unsupported format
+	///    and cannot be read or written by the current implementation.
+	/// </summary>
+	/// <example>
+	///    <para>Catching an exception when creating a <see
+	///    cref="File" />.</para>
+	///    <code lang="C#">
+	/// using System;
+	/// using TagLib;
+	///
+	/// public class ExceptionTest
+	/// {
+	/// 	public static void Main ()
+	/// 	{
+	/// 		try {
+	/// 			File file = File.Create ("myfile.flv"); // Not supported, YET!
+	/// 		} catch (UnsupportedFormatException e) {
+	/// 			Console.WriteLine ("That file format is not supported: {0}", e.ToString ());
+	/// 		}
+	///	}
+	/// }
+	///    </code>
+	///    <code lang="C++">
+	/// #using &lt;System.dll>
+	/// #using &lt;taglib-sharp.dll>
+	/// 
+	/// using System;
+	/// using TagLib;
+	///
+	/// void main ()
+	/// {
+	/// 	try {
+	/// 		File file = File::Create ("myfile.flv"); // Not supported, YET!
+	/// 	} catch (UnsupportedFormatException^ e) {
+	/// 		Console::WriteLine ("That file format is not supported: {0}", e);
+	/// 	}
+	/// }
+	///    </code>
+	///    <code lang="VB">
+	/// Imports System
+	/// Imports TagLib
+	///
+	/// Public Class ExceptionTest
+	/// 	Public Shared Sub Main ()
+	/// 		Try
+	/// 			file As File = File.Create ("myfile.flv") ' Not supported, YET!
+	/// 		Catch e As UnsupportedFormatException
+	/// 			Console.WriteLine ("That file format is not supported: {0}", e.ToString ());
+	/// 		End Try
+	///	End Sub
+	/// End Class
+	///    </code>
+	///    <code lang="Boo">
+	/// import System
+	/// import TagLib
+	///
+	/// try:
+	/// 	file As File = File.Create ("myfile.flv") # Not supported, YET!
+	/// catch e as UnsupportedFormatException:
+	/// 	Console.WriteLine ("That file format is not supported: {0}", e.ToString ());
+	///    </code>
+	/// </example>
+	[Serializable]
+	public class UnsupportedFormatException : Exception
+	{
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsupportedFormatException" /> with a specified
+		///    message.
+		/// </summary>
+		/// <param name="message">
+		///    A <see cref="string" /> containing a message explaining
+		///    the reason for the exception.
+		/// </param>
+		public UnsupportedFormatException (string message)
+			: base(message)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsupportedFormatException" /> with the default
+		///    values.
+		/// </summary>
+		public UnsupportedFormatException () : base()
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsupportedFormatException" /> with a specified
+		///    message containing a specified exception.
+		/// </summary>
+		/// <param name="message">
+		///    A <see cref="string" /> containing a message explaining
+		///    the reason for the exception.
+		/// </param>
+		/// <param name="innerException">
+		///    A <see cref="Exception" /> object to be contained in the
+		///    new exception. For example, previously caught exception.
+		/// </param>
+		public UnsupportedFormatException (string message,
+		                                   Exception innerException)
+			: base (message, innerException)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="UnsupportedFormatException" /> from a specified
+		///    serialization info and streaming context.
+		/// </summary>
+		/// <param name="info">
+		///    A <see cref="SerializationInfo" /> object containing the
+		///    serialized data to be used for the new instance.
+		/// </param>
+		/// <param name="context">
+		///    A <see cref="StreamingContext" /> object containing the
+		///    streaming context information for the new instance.
+		/// </param>
+		/// <remarks>
+		///    This constructor is implemented because <see
+		///    cref="UnsupportedFormatException" /> implements the <see
+		///    cref="ISerializable" /> interface.
+		/// </remarks>
+		protected UnsupportedFormatException (SerializationInfo info,
+		                                      StreamingContext context)
+			: base(info, context)
+		{
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/TagLib/TagLib/WavPack/File.cs b/lib/TagLib/TagLib/WavPack/File.cs
new file mode 100644
index 0000000..3e947df
--- /dev/null
+++ b/lib/TagLib/TagLib/WavPack/File.cs
@@ -0,0 +1,277 @@
+//
+// File.cs: Provides tagging and properties support for WavPack files.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   wvfile.cpp from libtunepimp
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// Copyright (C) 2006 by Lukáš Lalinský (Original Implementation)
+// Copyright (C) 2004 by Allan Sandfeld Jensen (Original 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
+//
+
+using System;
+
+namespace TagLib.WavPack {
+	/// <summary>
+	///    This class extends <see cref="TagLib.NonContainer.File" /> to
+	///    provide tagging and properties support for WavPack files.
+	/// </summary>
+	/// <remarks>
+	///    A <see cref="TagLib.Ape.Tag" /> will be added automatically to
+	///    any file that doesn't contain one. This change does not effect
+	///    the file and can be reversed using the following method:
+	///    <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
+	/// </remarks>
+	[SupportedMimeType("taglib/wv", "wv")]
+	[SupportedMimeType("audio/x-wavpack")]
+	public class File : TagLib.NonContainer.File
+	{
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the block with the audio header.
+		/// </summary>
+		private ByteVector header_block = null;
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system and specified read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path, ReadStyle propertiesStyle)
+			: base (path, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified path in the local file
+		///    system with an average read style.
+		/// </summary>
+		/// <param name="path">
+		///    A <see cref="string" /> object containing the path of the
+		///    file to use in the new instance.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="path" /> is <see langword="null" />.
+		/// </exception>
+		public File (string path) : base (path)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction and
+		///    specified read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction,
+		             ReadStyle propertiesStyle)
+			: base (abstraction, propertiesStyle)
+		{
+		}
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="File" /> for a specified file abstraction with an
+		///    average read style.
+		/// </summary>
+		/// <param name="abstraction">
+		///    A <see cref="IFileAbstraction" /> object to use when
+		///    reading from and writing to the file.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="abstraction" /> is <see langword="null"
+		///    />.
+		/// </exception>
+		public File (File.IFileAbstraction abstraction)
+			: base (abstraction)
+		{
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Methods
+		
+		/// <summary>
+		///    Gets a tag of a specified type from the current instance,
+		///    optionally creating a new tag if possible.
+		/// </summary>
+		/// <param name="type">
+		///    A <see cref="TagLib.TagTypes" /> value indicating the
+		///    type of tag to read.
+		/// </param>
+		/// <param name="create">
+		///    A <see cref="bool" /> value specifying whether or not to
+		///    try and create the tag if one is not found.
+		/// </param>
+		/// <returns>
+		///    A <see cref="Tag" /> object containing the tag that was
+		///    found in or added to the current instance. If no
+		///    matching tag was found and none was created, <see
+		///    langword="null" /> is returned.
+		/// </returns>
+		/// <remarks>
+		///    If a <see cref="TagLib.Id3v2.Tag" /> is added to the
+		///    current instance, it will be placed at the start of the
+		///    file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
+		///    <see cref="TagLib.Ape.Tag" /> will be added to the end of
+		///    the file. All other tag types will be ignored.
+		/// </remarks>
+		public override TagLib.Tag GetTag (TagTypes type, bool create)
+		{
+			Tag t = (Tag as TagLib.NonContainer.Tag).GetTag (type);
+			
+			if (t != null || !create)
+				return t;
+			
+			switch (type)
+			{
+			case TagTypes.Id3v1:
+				return EndTag.AddTag (type, Tag);
+			
+			case TagTypes.Id3v2:
+				return StartTag.AddTag (type, Tag);
+			
+			case TagTypes.Ape:
+				return EndTag.AddTag (type, Tag);
+			
+			default:
+				return null;
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region Protected Methods
+		
+		/// <summary>
+		///    Reads format specific information at the start of the
+		///    file.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadStart (long start,
+		                                   ReadStyle propertiesStyle)
+		{
+			if (header_block != null &&
+				propertiesStyle == ReadStyle.None)
+				return;
+				
+			Seek (start);
+			header_block = ReadBlock (
+				(int) StreamHeader.Size);
+		}
+		
+		/// <summary>
+		///    Reads format specific information at the end of the
+		///    file.
+		/// </summary>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		protected override void ReadEnd (long end,
+		                                 ReadStyle propertiesStyle)
+		{
+			// Make sure we have an APE tag.
+			GetTag (TagTypes.Ape, true);
+		}
+		
+		/// <summary>
+		///    Reads the audio properties from the file represented by
+		///    the current instance.
+		/// </summary>
+		/// <param name="start">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the tags end and the media data begins.
+		/// </param>
+		/// <param name="end">
+		///    A <see cref="long" /> value containing the seek position
+		///    at which the media data ends and the tags begin.
+		/// </param>
+		/// <param name="propertiesStyle">
+		///    A <see cref="ReadStyle" /> value specifying at what level
+		///    of accuracy to read the media properties, or <see
+		///    cref="ReadStyle.None" /> to ignore the properties.
+		/// </param>
+		/// <returns>
+		///    A <see cref="TagLib.Properties" /> object describing the
+		///    media properties of the file represented by the current
+		///    instance.
+		/// </returns>
+		protected override Properties ReadProperties (long start,
+		                                              long end,
+		                                              ReadStyle propertiesStyle)
+		{
+			StreamHeader header = new StreamHeader (header_block,
+				end - start);
+			return new Properties (TimeSpan.Zero, header);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/WavPack/StreamHeader.cs b/lib/TagLib/TagLib/WavPack/StreamHeader.cs
new file mode 100644
index 0000000..d8a4d0b
--- /dev/null
+++ b/lib/TagLib/TagLib/WavPack/StreamHeader.cs
@@ -0,0 +1,366 @@
+//
+// StreamHeader.cs: Provides support for reading WavPack audio properties.
+//
+// Author:
+//   Brian Nickel (brian nickel gmail com)
+//
+// Original Source:
+//   wvproperties.cpp from libtunepimp
+//
+// Copyright (C) 2006-2007 Brian Nickel
+// Copyright (C) 2006 by Lukáš Lalinský (Original Implementation)
+// Copyright (C) 2004 by Allan Sandfeld Jensen (Original 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
+//
+
+using System;
+
+namespace TagLib.WavPack {
+	/// <summary>
+	///    This struct implements <see cref="IAudioCodec" /> to provide
+	///    support for reading WavPack audio properties.
+	/// </summary>
+	public struct StreamHeader : IAudioCodec, ILosslessAudioCodec, IEquatable<StreamHeader>
+	{
+		#region Constants
+		
+		private static readonly uint [] sample_rates = new uint [] {
+			6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+			32000, 44100, 48000, 64000, 88200, 96000, 192000};
+		
+		private const int  BYTES_STORED = 3;
+		private const int  MONO_FLAG    = 4;
+		private const int  SHIFT_LSB   = 13;
+		private const long SHIFT_MASK  = (0x1fL << SHIFT_LSB);
+		private const int  SRATE_LSB   = 23;
+		private const long SRATE_MASK  = (0xfL << SRATE_LSB);
+		
+		#endregion
+		
+		
+		
+		#region Private Fields
+		
+		/// <summary>
+		///    Contains the number of bytes in the stream.
+		/// </summary>
+		private long stream_length;
+		
+		/// <summary>
+		///    Contains the WavPack version.
+		/// </summary>
+		private ushort version;
+		
+		/// <summary>
+		///    Contains the flags.
+		/// </summary>
+		private uint flags;
+		
+		/// <summary>
+		///    Contains the sample count.
+		/// </summary>
+		private uint samples;
+		
+		#endregion
+		
+		
+		#region Public Static Fields
+		
+		/// <summary>
+		///    The size of a WavPack header.
+		/// </summary>
+		public const uint Size = 32;
+		
+		/// <summary>
+		///    The identifier used to recognize a WavPack file.
+		/// </summary>
+		/// <value>
+		///    "wvpk"
+		/// </value>
+		public static readonly ReadOnlyByteVector FileIdentifier = "wvpk";
+		
+		#endregion
+		
+		
+		
+		#region Constructors
+		
+		/// <summary>
+		///    Constructs and initializes a new instance of <see
+		///    cref="StreamHeader" /> for a specified header block and
+		///    stream length.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="ByteVector" /> object containing the stream
+		///    header data.
+		/// </param>
+		/// <param name="streamLength">
+		///    A <see cref="long" /> value containing the length of the
+		///    WavPack stream in bytes.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		///    <paramref name="data" /> is <see langword="null" />.
+		/// </exception>
+		/// <exception cref="CorruptFileException">
+		///    <paramref name="data" /> does not begin with <see
+		///    cref="FileIdentifier" /> or is less than <see cref="Size"
+		///    /> bytes long.
+		/// </exception>
+		public StreamHeader (ByteVector data, long streamLength)
+		{
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			
+			if (!data.StartsWith (FileIdentifier))
+				throw new CorruptFileException (
+					"Data does not begin with identifier.");
+			
+			if (data.Count < Size)
+				throw new CorruptFileException (
+					"Insufficient data in stream header");
+			
+			stream_length = streamLength;
+			version = data.Mid (8, 2).ToUShort (false);
+			flags = data.Mid (24, 4).ToUInt (false);
+			samples = data.Mid (12, 4).ToUInt (false);
+		}
+		
+		#endregion
+		
+		
+		
+		#region Public Properties
+		
+		/// <summary>
+		///    Gets the duration of the media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TimeSpan" /> containing the duration of the
+		///    media represented by the current instance.
+		/// </value>
+		public TimeSpan Duration {
+			get {
+				return AudioSampleRate > 0 ?
+					TimeSpan.FromSeconds ((double) samples /
+						(double) AudioSampleRate + 0.5) :
+					TimeSpan.Zero;
+			}
+		}
+		
+		/// <summary>
+		///    Gets the types of media represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="MediaTypes.Audio" />.
+		/// </value>
+		public MediaTypes MediaTypes {
+			get {return MediaTypes.Audio;}
+		}
+		
+		/// <summary>
+		///    Gets a text description of the media represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> object containing a description
+		///    of the media represented by the current instance.
+		/// </value>
+		public string Description {
+			get {return string.Format (
+				System.Globalization.CultureInfo.InvariantCulture,
+				"WavPack Version {0} Audio", Version);}
+		}
+		
+		/// <summary>
+		///    Gets the bitrate of the audio represented by the current
+		///    instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing a bitrate of the
+		///    audio represented by the current instance.
+		/// </value>
+		public int AudioBitrate {
+			get {
+				return (int) (Duration > TimeSpan.Zero ?
+					((stream_length * 8L) /
+					Duration.TotalSeconds) / 1000 : 0);
+				}
+		}
+		
+		/// <summary>
+		///    Gets the sample rate of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the sample rate of
+		///    the audio represented by the current instance.
+		/// </value>
+		public int AudioSampleRate {
+			get {
+				return (int) (sample_rates [
+					(flags & SRATE_MASK) >> SRATE_LSB]);
+			}
+		}
+		
+		/// <summary>
+		///    Gets the number of channels in the audio represented by
+		///    the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of
+		///    channels in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int AudioChannels {
+			get {return ((flags & MONO_FLAG) != 0) ? 1 : 2;}
+		}
+		
+		/// <summary>
+		///    Gets the WavPack version of the audio represented by the
+		///    current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the WavPack version
+		///    of the audio represented by the current instance.
+		/// </value>
+		public int Version {
+			get {return version;}
+		}
+		
+		/// <summary>
+		///    Gets the number of bits per sample in the audio
+		///    represented by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="int" /> value containing the number of bits
+		///    per sample in the audio represented by the current
+		///    instance.
+		/// </value>
+		public int BitsPerSample {
+			get {
+				return (int) (((flags & BYTES_STORED) + 1) * 8 -
+					((flags & SHIFT_MASK) >> SHIFT_LSB));
+			}
+		}
+		
+		#endregion
+		
+		
+		
+		#region IEquatable
+		
+		/// <summary>
+		///    Generates a hash code for the current instance.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="int" /> value containing the hash code for
+		///    the current instance.
+		/// </returns>
+		public override int GetHashCode ()
+		{
+			unchecked {
+				return (int) (flags ^ samples ^ version);
+			}
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another object.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="object" /> to compare to the current
+		///    instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public override bool Equals (object other)
+		{
+			if (!(other is StreamHeader))
+				return false;
+			
+			return Equals ((StreamHeader) other);
+		}
+		
+		/// <summary>
+		///    Checks whether or not the current instance is equal to
+		///    another instance of <see cref="StreamHeader" />.
+		/// </summary>
+		/// <param name="other">
+		///    A <see cref="StreamHeader" /> object to compare to the
+		///    current instance.
+		/// </param>
+		/// <returns>
+		///    A <see cref="bool" /> value indicating whether or not the
+		///    current instance is equal to <paramref name="other" />.
+		/// </returns>
+		/// <seealso cref="M:System.IEquatable`1.Equals" />
+		public bool Equals (StreamHeader other)
+		{
+			return flags == other.flags &&
+				samples == other.samples &&
+				version == other.version;
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="StreamHeader" /> are equal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    The first <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    The second <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    equal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator == (StreamHeader first,
+		                                StreamHeader second)
+		{
+			return first.Equals (second);
+		}
+		
+		/// <summary>
+		///    Gets whether or not two instances of <see
+		///    cref="StreamHeader" /> are unequal to eachother.
+		/// </summary>
+		/// <param name="first">
+		///    The first <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <param name="second">
+		///    The second <see cref="StreamHeader" /> object to compare.
+		/// </param>
+		/// <returns>
+		///    <see langword="true" /> if <paramref name="first" /> is
+		///    unequal to <paramref name="second" />. Otherwise, <see
+		///    langword="false" />.
+		/// </returns>
+		public static bool operator != (StreamHeader first,
+		                                StreamHeader second)
+		{
+			return !first.Equals (second);
+		}
+		
+		#endregion
+	}
+}
diff --git a/lib/TagLib/TagLib/Xmp/XmlNodeExtensions.cs b/lib/TagLib/TagLib/Xmp/XmlNodeExtensions.cs
new file mode 100644
index 0000000..0accef5
--- /dev/null
+++ b/lib/TagLib/TagLib/Xmp/XmlNodeExtensions.cs
@@ -0,0 +1,97 @@
+//
+// XmlNodeExtensions.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Xml;
+
+namespace TagLib.Xmp
+{
+	internal static class XmlNodeExtensions
+	{
+		public static bool In (this XmlNode node, string ns)
+		{
+			return node.NamespaceURI == ns;
+		}
+
+		public static bool Is (this XmlNode node, string ns, string name)
+		{
+			return node.In (ns) && node.LocalName == name;
+		}
+
+		// 7.2.2 coreSyntaxTerms
+		//		rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID | rdf:datatype
+		public static bool IsCoreSyntax (this XmlNode node)
+		{
+			return node.In (XmpTag.RDF_NS) && (
+				node.LocalName == XmpTag.RDF_URI ||
+				node.LocalName == XmpTag.ID_URI ||
+				node.LocalName == XmpTag.ABOUT_URI ||
+				node.LocalName == XmpTag.PARSE_TYPE_URI ||
+				node.LocalName == XmpTag.RESOURCE_URI ||
+				node.LocalName == XmpTag.NODE_ID_URI ||
+				node.LocalName == XmpTag.DATA_TYPE_URI
+					);
+		}
+
+		// 7.2.4 oldTerms
+		//		rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID
+		public static bool IsOld (this XmlNode node)
+		{
+			return node.In (XmpTag.RDF_NS) && (
+				node.LocalName == XmpTag.ABOUT_EACH_URI ||
+				node.LocalName == XmpTag.ABOUT_EACH_PREFIX_URI ||
+				node.LocalName == XmpTag.BAG_ID_URI
+				);
+		}
+
+		// 7.2.5 nodeElementURIs
+		//		anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
+		public static bool IsNodeElement (this XmlNode node)
+		{
+			return !node.IsCoreSyntax () &&
+				!node.Is (XmpTag.RDF_NS, XmpTag.LI_URI) &&
+				!node.IsOld ();
+		}
+
+		// 7.2.6 propertyElementURIs
+		//		anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms )
+		public static bool IsPropertyElement (this XmlNode node)
+		{
+			return !node.IsCoreSyntax () &&
+				!node.Is (XmpTag.RDF_NS, XmpTag.DESCRIPTION_URI) &&
+				!node.IsOld ();
+		}
+
+		// 7.2.7 propertyAttributeURIs
+		//		anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
+		public static bool IsPropertyAttribute (this XmlNode node)
+		{
+			return node is XmlAttribute && 
+				!node.IsCoreSyntax () &&
+				!node.Is (XmpTag.RDF_NS, XmpTag.DESCRIPTION_URI) &&
+				!node.Is (XmpTag.RDF_NS, XmpTag.LI_URI) &&
+				!node.IsOld ();
+		}
+	}
+}
diff --git a/lib/TagLib/TagLib/Xmp/XmpNode.cs b/lib/TagLib/TagLib/Xmp/XmpNode.cs
new file mode 100644
index 0000000..b89a579
--- /dev/null
+++ b/lib/TagLib/TagLib/Xmp/XmpNode.cs
@@ -0,0 +1,431 @@
+//
+// XmpNode.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Xml;
+
+namespace TagLib.Xmp
+{
+	/// <summary>
+	///    An <see cref="XmpNode"/> represents a node in the XMP document.
+	///    This is any valid XMP element.
+	/// </summary>
+	public class XmpNode
+	{
+
+#region Private Fields
+		
+		/// <value>
+		///    The children of the current node
+		/// </value>
+		private List<XmpNode> children;
+	
+		/// <value>
+		///    The qualifiers of the current node
+		/// </value>
+		private Dictionary<string, Dictionary<string, XmpNode>> qualifiers;
+		
+		/// <value>
+		///    The name of the current node
+		/// </value>
+		private string name;
+
+#endregion
+
+#region Properties
+		
+		/// <value>
+		///    The namespace the current instance belongs to
+		/// </value>
+		public string Namespace { get; private set; }
+		
+		/// <value>
+		///    The name of the current node instance
+		/// </value>
+		public string Name {
+			get { return name; }
+			internal set {
+				if (name != null)
+					throw new Exception ("Cannot change named node");
+				
+				if (value == null)
+					throw new ArgumentException ("value");
+				
+				name = value;
+			}
+		}
+		
+		/// <value>
+		///    The text value of the current node
+		/// </value>
+		public string Value { get; set; }
+		
+		/// <value>
+		///    The type of the current node
+		/// </value>
+		public XmpNodeType Type { get; internal set; }
+
+		
+		/// <value>
+		///    The number of qualifiers of the current instance
+		/// </value>
+		public int QualifierCount {
+			get {
+				if (qualifiers == null)
+					return 0;
+				int count = 0;
+				foreach (var collection in qualifiers.Values) {
+					count += collection == null ? 0 : collection.Count;
+				}
+				return count;
+			}
+		}
+		
+		/// <value>
+		///    The children of the current instance.
+		/// </value>
+		public List<XmpNode> Children {
+			// TODO: do not return a list, because it can be modified elsewhere
+			get { return children ?? new List<XmpNode> (); }
+		}
+
+#endregion
+		
+#region Constructors
+		
+		/// <summary>
+		///    Constructor.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the new instance.
+		/// </param>
+		public XmpNode (string ns, string name)
+		{
+			// Namespaces in XMP need to end with / or #. Broken files are known
+			// to be floating around (we have one with MicrosoftPhoto in our tree).
+			// Correcting below.
+			if (ns != String.Empty && ns != XmpTag.XML_NS && !ns.EndsWith ("/") && !ns.EndsWith ("#"))
+				ns = String.Format ("{0}/", ns);
+
+			Namespace = ns;
+			Name = name;
+			Type = XmpNodeType.Simple;
+			Value = String.Empty;
+		}
+
+		/// <summary>
+		///    Constructor.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the new instance.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the new instance.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.String"/> with the txt value of the new instance.
+		/// </param>
+		public XmpNode (string ns, string name, string value) : this (ns, name)
+		{
+			Value = value;
+		}
+
+#endregion
+		
+#region Public Methods
+
+		/// <summary>
+		///    Adds a node as child of the current node
+		/// </summary>
+		/// <param name="node">
+		///    A <see cref="XmpNode"/> to be add as child
+		/// </param>
+		public void AddChild (XmpNode node)
+		{
+			if (node == null || node == this)
+				throw new ArgumentException ("node");
+			
+			if (children == null)
+				children = new List<XmpNode> ();
+			
+			children.Add (node);
+		}
+		
+		/// <summary>
+		///    Removes the given node as child of the current instance
+		/// </summary>
+		/// <param name="node">
+		///    A <see cref="XmpNode"/> to remove as child
+		/// </param>
+		public void RemoveChild (XmpNode node)
+		{
+			if (children == null)
+				return;
+			
+			children.Remove (node);
+		}
+
+		/// <summary>
+		///    Get a named child from the current node
+		/// </summary>
+		/// <param name="ns">
+		///    The namespace of the child node.
+		/// </param>
+		/// <param name="name">
+		///    The name of the child node.
+		/// </param>
+		/// <returns>
+		///    A <see cref="XmpNode"/> with the given name and namespace.
+		/// </returns>
+		public XmpNode GetChild (string ns, string name)
+		{
+			foreach (var node in children) {
+				if (node.Namespace.Equals (ns) && node.Name.Equals (name))
+					return node;
+			}
+			return null;
+		}
+
+		/// <summary>
+		///    Adds a node as qualifier of the current instance
+		/// </summary>
+		/// <param name="node">
+		///    A <see cref="XmpNode"/> to add as qualifier
+		/// </param>
+		public void AddQualifier (XmpNode node)
+		{
+			if (node == null || node == this)
+				throw new ArgumentException ("node");
+			
+			if (qualifiers == null)
+				qualifiers = new Dictionary<string, Dictionary<string, XmpNode>> ();
+			
+			if (!qualifiers.ContainsKey (node.Namespace))
+				qualifiers [node.Namespace] = new Dictionary<string, XmpNode> ();
+			
+			qualifiers [node.Namespace][node.Name] = node;
+		}
+
+		/// <summary>
+		///    Returns the qualifier associated with the given namespace <paramref name="ns"/>
+		///    and name <paramref name="name"/>
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the qualifier
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the qualifier
+		/// </param>
+		/// <returns>
+		///    A <see cref="XmpNode"/> with the qualifier
+		/// </returns>
+		public XmpNode GetQualifier (string ns, string name)
+		{
+			if (qualifiers == null)
+				return null;
+			if (!qualifiers.ContainsKey (ns))
+				return null;
+			if (!qualifiers [ns].ContainsKey (name))
+				return null;
+			return qualifiers [ns][name];
+		}
+		
+		/// <summary>
+		///    Print a debug output of the node.
+		/// </summary>
+		public void Dump ()
+		{
+			Dump ("");
+		}
+		
+		/// <summary>
+		///    Calls the Visitor for this node and every child node.
+		/// </summary>
+		/// <param name="visitor">
+		///    A <see cref="XmpNodeVisitor"/> to access the node and the children.
+		/// </param>
+		public void Accept (XmpNodeVisitor visitor)
+		{
+			visitor.Visit (this);
+			
+			// TODO: what is with the qualifiers ?
+			// either add them to be also visited, or add a comment
+			if (children != null) {
+				foreach (XmpNode child in children) {
+					child.Accept (visitor);
+				}
+			}
+		}
+		
+		/// <summary>
+		///    Renders the current instance as child of the given node to the
+		///    given <see cref="XmlNode"/>
+		/// </summary>
+		/// <param name="parent">
+		///    A <see cref="XmlNode"/> to render the current instance as child of.
+		/// </param>
+		public void RenderInto (XmlNode parent)
+		{
+			if (IsRootNode) {
+				AddAllChildrenTo (parent);
+
+			} else if (IsReallySimpleType && parent.Attributes.GetNamedItem (XmpTag.PARSE_TYPE_URI, XmpTag.RDF_NS) == null) {
+				// Simple values can be added as attributes of the parent node. Not allowed when the parent has an rdf:parseType.
+				XmlAttribute attr = XmpTag.CreateAttribute (parent.OwnerDocument, Name, Namespace);
+				attr.Value = Value;
+				parent.Attributes.Append (attr);
+
+			} else if (Type == XmpNodeType.Simple || Type == XmpNodeType.Struct) {
+				var node = XmpTag.CreateNode (parent.OwnerDocument, Name, Namespace);
+				node.InnerText = Value;
+
+				if (Type == XmpNodeType.Struct) {
+					// Structured types are always handled as a parseType=Resource node. This way, IsReallySimpleType will
+					// not match for child nodes, which makes sure they are added as extra nodes to this node. Does the
+					// trick well, unit tests that prove this are in XmpSpecTest.
+					XmlAttribute attr = XmpTag.CreateAttribute (parent.OwnerDocument, XmpTag.PARSE_TYPE_URI, XmpTag.RDF_NS);
+					attr.Value = "Resource";
+					node.Attributes.Append (attr);
+				}
+
+				AddAllQualifiersTo (node);
+				AddAllChildrenTo (node);
+				parent.AppendChild (node);
+
+			} else if (Type == XmpNodeType.Bag) {
+				var node = XmpTag.CreateNode (parent.OwnerDocument, Name, Namespace);
+				// TODO: Add all qualifiers.
+				if (QualifierCount > 0)
+					throw new NotImplementedException ();
+				var bag = XmpTag.CreateNode (parent.OwnerDocument, XmpTag.BAG_URI, XmpTag.RDF_NS);
+				foreach (var child in children)
+					child.RenderInto (bag);
+				node.AppendChild (bag);
+				parent.AppendChild (node);
+
+			} else if (Type == XmpNodeType.Alt) {
+				var node = XmpTag.CreateNode (parent.OwnerDocument, Name, Namespace);
+				// TODO: Add all qualifiers.
+				if (QualifierCount > 0)
+					throw new NotImplementedException ();
+				var bag = XmpTag.CreateNode (parent.OwnerDocument, XmpTag.ALT_URI, XmpTag.RDF_NS);
+				foreach (var child in children)
+					child.RenderInto (bag);
+				node.AppendChild (bag);
+				parent.AppendChild (node);
+
+			} else if (Type == XmpNodeType.Seq) {
+				var node = XmpTag.CreateNode (parent.OwnerDocument, Name, Namespace);
+				// TODO: Add all qualifiers.
+				if (QualifierCount > 0)
+					throw new NotImplementedException ();
+				var bag = XmpTag.CreateNode (parent.OwnerDocument, XmpTag.SEQ_URI, XmpTag.RDF_NS);
+				foreach (var child in children)
+					child.RenderInto (bag);
+				node.AppendChild (bag);
+				parent.AppendChild (node);
+
+			} else {
+				// Probably some combination of things we don't fully cover yet.
+				Dump ();
+				throw new NotImplementedException ();
+			}
+		}
+
+
+#endregion
+
+#region Internal Methods
+		
+		internal void Dump (string prefix) {
+			Console.WriteLine ("{0}{1}{2} ({4}) = \"{3}\"", prefix, Namespace, Name, Value, Type);
+			if (qualifiers != null) {
+				Console.WriteLine ("{0}Qualifiers:", prefix);
+
+				foreach (string ns in qualifiers.Keys) {
+					foreach (string name in qualifiers [ns].Keys) {
+						qualifiers [ns][name].Dump (prefix+"  ->  ");
+					}
+				}
+			}
+			if (children != null) {
+				Console.WriteLine ("{0}Children:", prefix);
+
+				foreach (XmpNode child in children) {
+					child.Dump (prefix+"  ->  ");
+				}
+			}
+		}	
+		
+#endregion
+		
+#region Private Methods
+
+		/// <summary>
+		///    Is this a node that we can transform into an attribute of the
+		///    parent node? Yes if it has no qualifiers or children, nor is
+		///    it part of a list.
+		/// </summary>
+		private bool IsReallySimpleType {
+			get {
+				return Type == XmpNodeType.Simple && (children == null || children.Count == 0) 
+					&& QualifierCount == 0 && (Name != XmpTag.LI_URI || Namespace != XmpTag.RDF_NS);
+			}
+		}
+
+		/// <summary>
+		///    Is this the root node of the tree?
+		/// </summary>
+		private bool IsRootNode {
+			get { return Name == String.Empty && Namespace == String.Empty; }
+		}
+
+		private void AddAllQualifiersTo (XmlNode xml)
+		{
+			if (qualifiers == null)
+				return;
+			foreach (var collection in qualifiers.Values) {
+				foreach (XmpNode node in collection.Values) {
+					XmlAttribute attr = XmpTag.CreateAttribute (xml.OwnerDocument, node.Name, node.Namespace);
+					attr.Value = node.Value;
+					xml.Attributes.Append (attr);
+				}
+			}
+		}
+		
+		private void AddAllChildrenTo (XmlNode parent)
+		{
+			if (children == null)
+				return;
+			foreach (var child in children)
+				child.RenderInto (parent);
+		}
+#endregion
+		
+		
+	}
+}
diff --git a/lib/TagLib/TagLib/Xmp/XmpNodeType.cs b/lib/TagLib/TagLib/Xmp/XmpNodeType.cs
new file mode 100644
index 0000000..ccb5f70
--- /dev/null
+++ b/lib/TagLib/TagLib/Xmp/XmpNodeType.cs
@@ -0,0 +1,59 @@
+//
+// XmpNodeType.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace TagLib.Xmp
+{
+	/// <summary>
+	///    Denotes the type of a node.
+	/// </summary>
+	public enum XmpNodeType
+	{
+		/// <summary>
+		///    Unstructured (simple) value node.
+		/// </summary>
+		Simple,
+
+		/// <summary>
+		///    Structured value node.
+		/// </summary>
+		Struct,
+
+		/// <summary>
+		///    Ordered array.
+		/// </summary>
+		Seq,
+
+		/// <summary>
+		///    Language alternative.
+		/// </summary>
+		Alt,
+
+		/// <summary>
+		///    Unordered structured value.
+		/// </summary>
+		Bag
+	}
+}
diff --git a/lib/TagLib/TagLib/Xmp/XmpNodeVisitor.cs b/lib/TagLib/TagLib/Xmp/XmpNodeVisitor.cs
new file mode 100644
index 0000000..c47b8f6
--- /dev/null
+++ b/lib/TagLib/TagLib/Xmp/XmpNodeVisitor.cs
@@ -0,0 +1,41 @@
+//
+// XmpNodeVisitor.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+namespace TagLib.Xmp
+{
+	/// <summary>
+	///    A visitor that walks the XMP node tree. This can be used to
+	///    perform cleanups of XMP data. See the Visitor pattern for
+	///    more info if you don't know how to use this.
+	/// </summary>
+	public interface XmpNodeVisitor
+	{
+		/// <summary>
+		///    Visit an <see cref="XmpNode" />.
+		/// </summary>
+		/// <param name="node">
+		///    The <see cref="XmpNode" /> that is being visited.
+		/// </param>
+		void Visit (XmpNode node);
+	}
+}
diff --git a/lib/TagLib/TagLib/Xmp/XmpTag.cs b/lib/TagLib/TagLib/Xmp/XmpTag.cs
new file mode 100644
index 0000000..1a1d511
--- /dev/null
+++ b/lib/TagLib/TagLib/Xmp/XmpTag.cs
@@ -0,0 +1,1111 @@
+//
+// XmpTag.cs:
+//
+// Author:
+//   Ruben Vermeersch (ruben savanne be)
+//
+// Copyright (C) 2009 Ruben Vermeersch
+//
+// 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
+//
+
+using System;
+using System.Collections.Generic;
+using System.Xml;
+
+using TagLib.Image;
+
+namespace TagLib.Xmp
+{
+	/// <summary>
+	///    Holds XMP (Extensible Metadata Platform) metadata.
+	/// </summary>
+	public class XmpTag : ImageTag
+	{
+#region Parsing speedup
+		private Dictionary<string, Dictionary<string, XmpNode>> nodes;
+
+		/// <summary>
+		///    Adobe namespace
+		/// </summary>
+		public static readonly string ADOBE_X_NS = "adobe:ns:meta/";
+
+		/// <summary>
+		///    Camera Raw Settings namespace
+		/// </summary>
+		public static readonly string CRS_NS = "http://ns.adobe.com/camera-raw-settings/1.0/";;
+
+		/// <summary>
+		///    Dublin Core namespace
+		/// </summary>
+		public static readonly string DC_NS = "http://purl.org/dc/elements/1.1/";;
+
+		/// <summary>
+		///    Exif namespace
+		/// </summary>
+		public static readonly string EXIF_NS = "http://ns.adobe.com/exif/1.0/";;
+
+		/// <summary>
+		///    Exif aux namespace
+		/// </summary>
+		public static readonly string EXIF_AUX_NS = "http://ns.adobe.com/exif/1.0/aux/";;
+
+		/// <summary>
+		///    JOB namespace
+		/// </summary>
+		public static readonly string JOB_NS = "http://ns.adobe.com/xap/1.0/sType/Job#";;
+
+		/// <summary>
+		///    Microsoft Photo namespace
+		/// </summary>
+		public static readonly string MS_PHOTO_NS = "http://ns.microsoft.com/photo/1.0/";;
+
+		/// <summary>
+		///    Photoshop namespace
+		/// </summary>
+		public static readonly string PHOTOSHOP_NS = "http://ns.adobe.com/photoshop/1.0/";;
+
+		/// <summary>
+		///    RDF namespace
+		/// </summary>
+		public static readonly string RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";;
+
+		/// <summary>
+		///    STDIM namespace
+		/// </summary>
+		public static readonly string STDIM_NS = "http://ns.adobe.com/xap/1.0/sType/Dimensions#";;
+
+		/// <summary>
+		///    TIFF namespace
+		/// </summary>
+		public static readonly string TIFF_NS = "http://ns.adobe.com/tiff/1.0/";;
+
+		/// <summary>
+		///    XAP (XMP's previous name) namespace
+		/// </summary>
+		public static readonly string XAP_NS = "http://ns.adobe.com/xap/1.0/";;
+
+		/// <summary>
+		///    XAP bj namespace
+		/// </summary>
+		public static readonly string XAP_BJ_NS = "http://ns.adobe.com/xap/1.0/bj/";;
+
+		/// <summary>
+		///    XAP mm namespace
+		/// </summary>
+		public static readonly string XAP_MM_NS = "http://ns.adobe.com/xap/1.0/mm/";;
+
+		/// <summary>
+		///    XAP rights namespace
+		/// </summary>
+		public static readonly string XAP_RIGHTS_NS = "http://ns.adobe.com/xap/1.0/rights/";;
+
+		/// <summary>
+		///    XML namespace
+		/// </summary>
+		public static readonly string XML_NS = "http://www.w3.org/XML/1998/namespace";;
+
+		/// <summary>
+		///    XMLNS namespace
+		/// </summary>
+		public static readonly string XMLNS_NS = "http://www.w3.org/2000/xmlns/";;
+
+		/// <summary>
+		///    XMP TPg (XMP Paged-Text) namespace
+		/// </summary>
+		public static readonly string XMPTG_NS = "http://ns.adobe.com/xap/1.0/t/pg/";;
+
+		internal static readonly string ABOUT_URI = "about";
+		internal static readonly string ABOUT_EACH_URI = "aboutEach";
+		internal static readonly string ABOUT_EACH_PREFIX_URI = "aboutEachPrefix";
+		internal static readonly string ALT_URI = "Alt";
+		internal static readonly string BAG_URI = "Bag";
+		internal static readonly string BAG_ID_URI = "bagID";
+		internal static readonly string DATA_TYPE_URI = "datatype";
+		internal static readonly string DESCRIPTION_URI = "Description";
+		internal static readonly string ID_URI = "ID";
+		internal static readonly string LANG_URI = "lang";
+		internal static readonly string LI_URI = "li";
+		internal static readonly string NODE_ID_URI = "nodeID";
+		internal static readonly string PARSE_TYPE_URI = "parseType";
+		internal static readonly string RDF_URI = "RDF";
+		internal static readonly string RESOURCE_URI = "resource";
+		internal static readonly string SEQ_URI = "Seq";
+		internal static readonly string VALUE_URI = "value";
+
+		// This allows for fast string comparison using operator==
+		static readonly NameTable NameTable = new NameTable ();
+		static bool initialized = false;
+
+		void Initialize ()
+		{
+			if (initialized)
+				return;
+
+			lock (NameTable) {
+				if (initialized)
+					return;
+				PrepareNamespaces ();
+				initialized = true;
+			}
+		}
+
+		void PrepareNamespaces ()
+		{
+			// Namespaces
+			AddNamespacePrefix ("", ""); // Needed for the about attribute, which can be unqualified.
+			AddNamespacePrefix ("x", ADOBE_X_NS);
+			AddNamespacePrefix ("crs", CRS_NS);
+			AddNamespacePrefix ("dc", DC_NS);
+			AddNamespacePrefix ("exif", EXIF_NS);
+			AddNamespacePrefix ("aux", EXIF_AUX_NS);
+			AddNamespacePrefix ("stJob", JOB_NS);
+			AddNamespacePrefix ("MicrosoftPhoto", MS_PHOTO_NS);
+			AddNamespacePrefix ("photoshop", PHOTOSHOP_NS);
+			AddNamespacePrefix ("rdf", RDF_NS);
+			AddNamespacePrefix ("stDim", STDIM_NS);
+			AddNamespacePrefix ("tiff", TIFF_NS);
+			AddNamespacePrefix ("xmp", XAP_NS);
+			AddNamespacePrefix ("xapBJ", XAP_BJ_NS);
+			AddNamespacePrefix ("xapMM", XAP_MM_NS);
+			AddNamespacePrefix ("xapRights", XAP_RIGHTS_NS);
+			AddNamespacePrefix ("xml", XML_NS);
+			AddNamespacePrefix ("xmlns", XMLNS_NS);
+			AddNamespacePrefix ("xmpTPg", XMPTG_NS);
+
+			// Attribute names
+			NameTable.Add (ABOUT_URI);
+			NameTable.Add (ABOUT_EACH_URI);
+			NameTable.Add (ABOUT_EACH_PREFIX_URI);
+			NameTable.Add (ALT_URI);
+			NameTable.Add (BAG_URI);
+			NameTable.Add (BAG_ID_URI);
+			NameTable.Add (DATA_TYPE_URI);
+			NameTable.Add (DESCRIPTION_URI);
+			NameTable.Add (ID_URI);
+			NameTable.Add (LANG_URI);
+			NameTable.Add (LI_URI);
+			NameTable.Add (NODE_ID_URI);
+			NameTable.Add (PARSE_TYPE_URI);
+			NameTable.Add (RDF_URI);
+			NameTable.Add (RESOURCE_URI);
+			NameTable.Add (SEQ_URI);
+			NameTable.Add (VALUE_URI);
+		}
+
+		/// <summary>
+		///    Mapping between full namespaces and their short prefix. Needs to be public for the unit test generator.
+		/// </summary>
+		public static Dictionary<string, string> NamespacePrefixes = new Dictionary<string, string>();
+
+		static int anon_ns_count = 0;
+
+		static void AddNamespacePrefix (string prefix, string ns)
+		{
+			NameTable.Add (ns);
+			NamespacePrefixes.Add (ns, prefix);
+		}
+
+#endregion
+
+#region Constructors
+
+		/// <summary>
+		///    Construct a new empty <see cref="XmpTag"/>.
+		/// </summary>
+		public XmpTag ()
+		{
+			Initialize ();
+			NodeTree = new XmpNode (String.Empty, String.Empty);
+			nodes = new Dictionary<string, Dictionary<string, XmpNode>> ();
+		}
+		
+		/// <summary>
+		///    Construct a new <see cref="XmpTag"/>, using the data parsed from the given string.
+		/// </summary>
+		/// <param name="data">
+		///    A <see cref="System.String"/> containing an XMP packet. This should be a valid
+		///    XMP block.
+		/// </param>
+		public XmpTag (string data)
+		{
+			Initialize ();
+			XmlDocument doc = new XmlDocument (NameTable);
+			doc.LoadXml (data);
+
+			XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
+			nsmgr.AddNamespace("x", ADOBE_X_NS);
+			nsmgr.AddNamespace("rdf", RDF_NS);
+
+			XmlNode node = doc.SelectSingleNode("/x:xmpmeta/rdf:RDF", nsmgr);
+			// Old versions of XMP were called XAP, fall back to this case (tested in sample_xap.jpg)
+			node = node ?? doc.SelectSingleNode("/x:xapmeta/rdf:RDF", nsmgr);
+			if (node == null)
+				throw new CorruptFileException ();
+
+			NodeTree = ParseRDF (node);
+			NodeTree.Accept (new NodeIndexVisitor (this));
+			//NodeTree.Dump ();
+			//Console.WriteLine (node.OuterXml);
+		}
+
+#endregion
+		
+#region Private Methods
+
+		// 7.2.9 RDF
+		//		start-element ( URI == rdf:RDF, attributes == set() )
+		//		nodeElementList
+		//		end-element()
+		private XmpNode ParseRDF (XmlNode rdf_node)
+		{
+			XmpNode top = new XmpNode (String.Empty, String.Empty);
+			foreach (XmlNode node in rdf_node.ChildNodes) {
+				if (node is XmlWhitespace)
+					continue;
+				
+				if (node.Is (RDF_NS, DESCRIPTION_URI)) {
+					var attr = node.Attributes.GetNamedItem (RDF_NS, ABOUT_URI) as XmlAttribute;
+					if (attr != null) {
+						if (top.Name != String.Empty && top.Name != attr.InnerText)
+							throw new CorruptFileException ("Multiple inconsistent rdf:about values!");
+						top.Name = attr.InnerText;
+					}
+					continue;
+				}
+
+				throw new CorruptFileException ("Cannot have anything other than rdf:Description at the top level");
+			}
+			ParseNodeElementList (top, rdf_node);
+			return top;
+		}
+
+		// 7.2.10 nodeElementList
+		//		ws* ( nodeElement ws* )*
+		private void ParseNodeElementList (XmpNode parent, XmlNode xml_parent)
+		{
+			foreach (XmlNode node in xml_parent.ChildNodes) {
+				if (node is XmlWhitespace)
+					continue;
+				ParseNodeElement (parent, node);
+			}
+		}
+
+		// 7.2.11 nodeElement
+		//		start-element ( URI == nodeElementURIs,
+		//						attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
+		//		propertyEltList
+		//		end-element()
+		//
+		// 7.2.13 propertyEltList
+		//		ws* ( propertyElt ws* )*
+		private void ParseNodeElement (XmpNode parent, XmlNode node)
+		{
+			if (!node.IsNodeElement ())
+				throw new CorruptFileException ("Unexpected node found, invalid RDF?");
+
+			if (node.Is (RDF_NS, SEQ_URI)) {
+				parent.Type = XmpNodeType.Seq;
+			} else if (node.Is (RDF_NS, ALT_URI)) {
+				parent.Type = XmpNodeType.Alt;
+			} else if (node.Is (RDF_NS, BAG_URI)) {
+				parent.Type = XmpNodeType.Bag;
+			} else if (node.Is (RDF_NS, DESCRIPTION_URI)) {
+				parent.Type = XmpNodeType.Struct;
+			} else {
+				throw new Exception ("Unknown nodeelement found! Perhaps an unimplemented collection?");
+			}
+
+			foreach (XmlAttribute attr in node.Attributes) {
+				if (attr.In (XMLNS_NS))
+					continue;
+				if (attr.Is (RDF_NS, ID_URI) || attr.Is (RDF_NS, NODE_ID_URI) || attr.Is (RDF_NS, ABOUT_URI))
+					continue;
+				if (attr.Is (XML_NS, LANG_URI))
+					throw new CorruptFileException ("xml:lang is not allowed here!");
+				parent.AddChild (new XmpNode (attr.NamespaceURI, attr.LocalName, attr.InnerText));
+			}
+
+			foreach (XmlNode child in node.ChildNodes) {
+				if (child is XmlWhitespace || child is XmlComment)
+					continue;
+				ParsePropertyElement (parent, child);
+			}
+		}
+
+		// 7.2.14 propertyElt
+		//		resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
+		//		parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt |
+		//		parseTypeOtherPropertyElt | emptyPropertyElt
+		private void ParsePropertyElement (XmpNode parent, XmlNode node)
+		{
+			int count = 0;
+			bool has_other = false;
+			foreach (XmlAttribute attr in node.Attributes) {
+				if (!attr.In (XMLNS_NS))
+					count++;
+
+				if (!attr.Is (XML_NS, LANG_URI) && !attr.Is (RDF_NS, ID_URI) && !attr.In (XMLNS_NS))
+					has_other = true;
+			}
+
+			if (count > 3) {
+				ParseEmptyPropertyElement (parent, node);
+			} else {
+				if (!has_other) {
+					if (!node.HasChildNodes) {
+						ParseEmptyPropertyElement (parent, node);
+					} else {
+						bool only_text = true;
+						foreach (XmlNode child in node.ChildNodes) {
+							if (!(child is XmlText))
+								only_text = false;
+						}
+
+						if (only_text) {
+							ParseLiteralPropertyElement (parent, node);
+						} else {
+							ParseResourcePropertyElement (parent, node);
+						}
+					}
+				} else {
+					foreach (XmlAttribute attr in node.Attributes) {
+						if (attr.Is (XML_NS, LANG_URI) || attr.Is (RDF_NS, ID_URI) || attr.In (XMLNS_NS))
+							continue;
+
+						if (attr.Is (RDF_NS, DATA_TYPE_URI)) {
+							ParseLiteralPropertyElement (parent, node);
+						} else if (!attr.Is (RDF_NS, PARSE_TYPE_URI)) {
+							ParseEmptyPropertyElement (parent, node);
+						} else if (attr.InnerText.Equals ("Resource")) {
+							ParseTypeResourcePropertyElement (parent, node);
+						} else {
+							// Neither Literal, Collection or anything else is allowed
+							throw new CorruptFileException (String.Format ("This is not allowed in XMP! Bad XMP: {0}", node.OuterXml));
+						}
+					}
+				}
+			}
+		}
+
+		// 7.2.15 resourcePropertyElt
+		//		start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
+		//		ws* nodeElement ws*
+		//		end-element()
+		private void ParseResourcePropertyElement (XmpNode parent, XmlNode node)
+		{
+			if (!node.IsPropertyElement ())
+				throw new CorruptFileException ("Invalid property");
+
+			XmpNode new_node = new XmpNode (node.NamespaceURI, node.LocalName);
+			foreach (XmlAttribute attr in node.Attributes) {
+				if (attr.Is (XML_NS, LANG_URI)) {
+					new_node.AddQualifier (new XmpNode (XML_NS, LANG_URI, attr.InnerText));
+				} else if (attr.Is (RDF_NS, ID_URI) || attr.In (XMLNS_NS)) {
+					continue;
+				}
+
+				throw new CorruptFileException (String.Format ("Invalid attribute: {0}", attr.OuterXml));
+			}
+
+			bool has_xml_children = false;
+			foreach (XmlNode child in node.ChildNodes) {
+				if (child is XmlWhitespace)
+					continue;
+				if (child is XmlText)
+					throw new CorruptFileException ("Can't have text here!");
+				has_xml_children = true;
+
+				ParseNodeElement (new_node, child);
+			}
+
+			if (!has_xml_children)
+				throw new CorruptFileException ("Missing children for resource property element");
+
+			parent.AddChild (new_node);
+		}
+
+		// 7.2.16 literalPropertyElt
+		//		start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
+		//		text()
+		//		end-element()
+		private void ParseLiteralPropertyElement (XmpNode parent, XmlNode node)
+		{
+			if (!node.IsPropertyElement ())
+				throw new CorruptFileException ("Invalid property");
+			parent.AddChild (CreateTextPropertyWithQualifiers (node, node.InnerText));
+		}
+
+		// 7.2.18 parseTypeResourcePropertyElt
+		//		start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
+		//		propertyEltList
+		//		end-element()
+		private void ParseTypeResourcePropertyElement (XmpNode parent, XmlNode node)
+		{
+			if (!node.IsPropertyElement ())
+				throw new CorruptFileException ("Invalid property");
+
+			XmpNode new_node = new XmpNode (node.NamespaceURI, node.LocalName);
+			new_node.Type = XmpNodeType.Struct;
+
+			foreach (XmlNode attr in node.Attributes) {
+				if (attr.Is (XML_NS, LANG_URI))
+					new_node.AddQualifier (new XmpNode (XML_NS, LANG_URI, attr.InnerText));
+			}
+
+			foreach (XmlNode child in node.ChildNodes) {
+				if (child is XmlWhitespace || child is XmlComment)
+					continue;
+				ParsePropertyElement (new_node, child);
+			}
+
+			parent.AddChild (new_node);
+		}
+
+		// 7.2.21 emptyPropertyElt
+		//		start-element ( URI == propertyElementURIs,
+		//						attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
+		//		end-element()
+		private void ParseEmptyPropertyElement (XmpNode parent, XmlNode node)
+		{
+			if (!node.IsPropertyElement ())
+				throw new CorruptFileException ("Invalid property");
+			if (node.HasChildNodes)
+				throw new CorruptFileException (String.Format ("Can't have content in this node! Node: {0}", node.OuterXml));
+
+			var rdf_value = node.Attributes.GetNamedItem (VALUE_URI, RDF_NS) as XmlAttribute;
+			var rdf_resource = node.Attributes.GetNamedItem (RESOURCE_URI, RDF_NS) as XmlAttribute;
+			
+			// Options 1 and 2
+			var simple_prop_val = rdf_value ?? rdf_resource ?? null;
+			if (simple_prop_val != null) {
+				string value = simple_prop_val.InnerText;
+				node.Attributes.Remove (simple_prop_val); // This one isn't a qualifier.
+				parent.AddChild (CreateTextPropertyWithQualifiers (node, value));
+				return;
+			}
+
+			// Options 3 & 4
+			var new_node = new XmpNode (node.NamespaceURI, node.LocalName);
+			foreach (XmlAttribute a in node.Attributes) {
+				if (a.Is(RDF_NS, ID_URI) || a.Is(RDF_NS, NODE_ID_URI)) {
+					continue;
+				} else if (a.In (XMLNS_NS)) {
+					continue;
+				} else if (a.Is (XML_NS, LANG_URI)) {
+					new_node.AddQualifier (new XmpNode (XML_NS, LANG_URI, a.InnerText));
+				}
+
+				new_node.AddChild (new XmpNode (a.NamespaceURI, a.LocalName, a.InnerText));
+			}
+			parent.AddChild (new_node);
+		}
+
+		private XmpNode CreateTextPropertyWithQualifiers (XmlNode node, string value)
+		{
+			XmpNode t = new XmpNode (node.NamespaceURI, node.LocalName, value);
+			foreach (XmlAttribute attr in node.Attributes) {
+				if (attr.In (XMLNS_NS))
+					continue;
+				t.AddQualifier (new XmpNode (attr.NamespaceURI, attr.LocalName, attr.InnerText));
+			}
+			return t;
+		}
+		
+		private XmpNode NewNode (string ns, string name)
+		{
+			Dictionary <string, XmpNode> ns_nodes = null;
+			
+			if (!nodes.ContainsKey (ns)) {
+				ns_nodes = new Dictionary <string, XmpNode> ();
+				nodes.Add (ns, ns_nodes);
+			
+			} else
+				ns_nodes = nodes [ns];
+			
+			if (ns_nodes.ContainsKey (name)) {
+				foreach (XmpNode child_node in NodeTree.Children) {
+					if (child_node.Namespace == ns && child_node.Name == name) {
+						NodeTree.RemoveChild (child_node);
+						break;
+					}
+				}
+				
+				ns_nodes.Remove (name);
+			}
+			
+			XmpNode node = new XmpNode (ns, name);
+			ns_nodes.Add (name, node);
+			
+			NodeTree.AddChild (node);
+			
+			return node;
+		}
+		
+		private XmpNode NewNode (string ns, string name, XmpNodeType type)
+		{
+			XmpNode node = NewNode (ns, name);
+			node.Type = type;
+			
+			return node;
+		}
+		
+		private void RemoveNode (string ns, string name)
+		{
+			if (!nodes.ContainsKey (ns))
+				return;
+			
+			foreach (XmpNode node in NodeTree.Children) {
+				if (node.Namespace == ns && node.Name == name) {
+					NodeTree.RemoveChild (node);
+					break;
+				}
+			}
+			
+			nodes[ns].Remove (name);
+		}
+
+#endregion
+
+#region Public Properties
+
+		/// <summary>
+		///    Gets the tag types contained in the current instance.
+		/// </summary>
+		/// <value>
+		///    Always <see cref="TagTypes.XMP" />.
+		/// </value>
+		public override TagTypes TagTypes {
+			get {return TagTypes.XMP;}
+		}
+
+		/// <summary>
+		///    Get the tree of <see cref="XmpNode" /> nodes. These contain the values
+		///    parsed from the XMP file.
+		/// </summary>
+		public XmpNode NodeTree {
+			get; private set;
+		}
+
+#endregion
+
+#region Public Methods
+		
+		/// <summary>
+		///    Clears the values stored in the current instance.
+		/// </summary>
+		public override void Clear ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		/// <summary>
+		///    Finds the node associated with the namespace <paramref name="ns"/> and the name
+		///    <paramref name="name"/>.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the node.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the node.
+		/// </param>
+		/// <returns>
+		///    A <see cref="XmpNode"/> with the found node, or <see langword="null"/>
+		///    if no node was found.
+		/// </returns>
+		public XmpNode FindNode (string ns, string name)
+		{
+			if (!nodes.ContainsKey (ns))
+				return null;
+			if (!nodes [ns].ContainsKey (name))
+				return null;
+			return nodes [ns][name];
+
+		}
+		
+		/// <summary>
+		///    Returns the text of the node associated with the namespace
+		///    <param name="ns"/> and the name <paramref name="name"/>.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the node.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the node.
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.String"/> with the text of the node, or
+		///    <see langword="null"/> if no such node exists, or if it is not
+		///    a text node.
+		/// </returns>
+		public string GetTextNode (string ns, string name)
+		{
+			var node = FindNode (ns, name);
+			
+			if (node == null || node.Type != XmpNodeType.Simple)
+				return null;
+			
+			return node.Value;
+		}
+		
+		/// <summary>
+		///    Creates a new text node associated with the namespace
+		///    <paramref name="ns"/> and the name <paramref name="name"/>.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the node.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the node.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.String"/> with the value for the new node.
+		///    If <see langword="null"/> is given, a possibly existing node will
+		///    be deleted.
+		/// </param>
+		public void SetTextNode (string ns, string name, string value)
+		{
+			if (value == null) {
+				RemoveNode (ns, name);
+				return;
+			}
+			
+			var node = NewNode (ns, name);
+			node.Value = value;
+		}
+		
+		/// <summary>
+		///    Searches for a node holding language alternatives. The return value
+		///    is the value of the default language stored by the node. The node is
+		///    identified by the namespace <paramref name="ns"/> and the name
+		///    <paramref name="name"/>. If the default language is not set, an arbitrary
+		///    one is chosen.
+		///    It is also tried to return the value a simple text node, if no
+		///    associated alt-node exists.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the node.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the node.
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.String"/> with the value stored as default language
+		///    for the referenced node.
+		/// </returns>
+		public string GetLangAltNode (string ns, string name)
+		{
+			var node = FindNode (ns, name);
+			
+			if (node == null)
+				return null;
+			
+			if (node.Type == XmpNodeType.Simple)
+				return node.Value;
+			
+			if (node.Type != XmpNodeType.Alt)
+				return null;
+			
+			var children = node.Children;
+			foreach (XmpNode child_node in children) {
+				var qualifier = child_node.GetQualifier (XML_NS, "lang");
+				if (qualifier != null && qualifier.Value == "x-default")
+					return child_node.Value;
+			}
+			
+			if (children.Count > 0 && children[0].Type == XmpNodeType.Simple)
+				return children[0].Value;
+			
+			return null;
+		}
+		
+		/// <summary>
+		///    Stores a the given <paramref name="value"/> as the default language
+		///    value for the alt-node associated with the namespace
+		///    <paramref name="ns"/> and the name <paramref name="name"/>.
+		///    All other alternatives set, are deleted by this method.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the node.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the node.
+		/// </param>
+		/// <param name="value">
+		///    A <see cref="System.String"/> with the value for the default language
+		///    to set. If <see langword="null"/> is given, a possibly existing node
+		///    will be deleted.
+		/// </param>
+		public void SetLangAltNode (string ns, string name, string value)
+		{
+			if (value == null) {
+				RemoveNode (ns, name);
+				return;
+			}
+			
+			var node = NewNode (ns, name, XmpNodeType.Alt);
+			
+			var child_node = new XmpNode (RDF_NS, LI_URI, value);
+			child_node.AddQualifier (new XmpNode (XML_NS, "lang", "x-default"));
+			
+			node.AddChild (child_node);
+		}
+		
+		/// <summary>
+		///    The method returns an array of <see cref="System.String"/> values
+		///    which are the stored text of the child nodes of the node associated
+		///    with the namespace <paramref name="ns"/> and the name <paramref name="name"/>.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the node.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the node.
+		/// </param>
+		/// <returns>
+		///    A <see cref="System.String[]"/> with the text stored in the child nodes.
+		/// </returns>
+		public string[] GetCollectionNode (string ns, string name)
+		{
+			var node = FindNode (ns, name);
+			
+			if (node == null)
+				return null;
+			
+			List<string> items = new List<string> ();
+				
+			foreach (XmpNode child in node.Children) {
+				
+				string item = child.Value;
+				if (item != null)
+					items.Add (item);
+			}
+			
+			return items.ToArray ();
+		}
+		
+		/// <summary>
+		///    Sets a <see cref="System.String[]"/> as texts to the children of the
+		///    node associated with the namespace <paramref name="ns"/> and the name
+		///    <paramref name="name"/>.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace of the node.
+		/// </param>
+		/// <param name="name">
+		///    A <see cref="System.String"/> with the name of the node.
+		/// </param>
+		/// <param name="values">
+		///    A <see cref="System.String[]"/> with the values to set for the children.
+		/// </param>
+		/// <param name="type">
+		///    A <see cref="XmpNodeType"/> with the type of the parent node.
+		/// </param>
+		public void SetCollectionNode (string ns, string name, string [] values, XmpNodeType type)
+		{
+			if (type == XmpNodeType.Simple || type == XmpNodeType.Alt)
+				throw new ArgumentException ("type");
+			
+			if (values == null) {
+				RemoveNode (ns, name);
+				return;
+			}
+			
+			var node = NewNode (ns, name, type);
+			foreach (string value in values)
+				node.AddChild (new XmpNode (RDF_NS, LI_URI, value));
+		}
+
+		/// <summary>
+		///    Renders the current instance to an XMP <see cref="System.String"/>.
+		/// </summary>
+		/// <returns>
+		///    A <see cref="System.String"/> with the XMP structure.
+		/// </returns>
+		public string Render ()
+		{
+			XmlDocument doc = new XmlDocument (NameTable);
+			var meta = CreateNode (doc, "xmpmeta", ADOBE_X_NS);
+			var rdf = CreateNode (doc, "RDF", RDF_NS);
+			var description = CreateNode (doc, "Description", RDF_NS);
+			NodeTree.RenderInto (description);
+			doc.AppendChild (meta);
+			meta.AppendChild (rdf);
+			rdf.AppendChild (description);
+			return doc.OuterXml;
+		}
+
+		/// <summary>
+		///    Make sure there's a suitable prefix mapped for the given namespace URI.
+		/// </summary>
+		/// <param name="ns">
+		///    A <see cref="System.String"/> with the namespace that will be rendered.
+		/// </param>
+		static void EnsureNamespacePrefix (string ns)
+		{
+			if (!NamespacePrefixes.ContainsKey (ns)) {
+				NamespacePrefixes.Add (ns, String.Format ("ns{0}", ++anon_ns_count));
+				Console.WriteLine ("TAGLIB# DEBUG: Added {0} prefix for {1} namespace (XMP)", NamespacePrefixes [ns], ns);
+			}
+		}
+
+		internal static XmlNode CreateNode (XmlDocument doc, string name, string ns)
+		{
+			EnsureNamespacePrefix (ns);
+			return doc.CreateElement (NamespacePrefixes [ns], name, ns);
+		}
+
+		internal static XmlAttribute CreateAttribute (XmlDocument doc, string name, string ns)
+		{
+			EnsureNamespacePrefix (ns);
+			return doc.CreateAttribute (NamespacePrefixes [ns], name, ns);
+		}
+
+#endregion
+
+		private class NodeIndexVisitor : XmpNodeVisitor
+		{
+			private XmpTag tag;
+
+			public NodeIndexVisitor (XmpTag tag) {
+				this.tag = tag;
+			}
+
+			public void Visit (XmpNode node)
+			{
+				// TODO: This should be a proper check to see if it is a nodeElement
+				if (node.Namespace == XmpTag.RDF_NS && node.Name == XmpTag.LI_URI)
+					return;
+
+				AddNode (node);
+			}
+
+			void AddNode (XmpNode node)
+			{
+				if (tag.nodes == null)
+					tag.nodes = new Dictionary<string, Dictionary<string, XmpNode>> ();
+				if (!tag.nodes.ContainsKey (node.Namespace))
+					tag.nodes [node.Namespace] = new Dictionary<string, XmpNode> ();
+
+				tag.nodes [node.Namespace][node.Name] = node;
+			}
+		}
+
+#region Metadata fields
+
+		/// <summary>
+		///    Gets or sets the comment for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the comment of the
+		///    current instace.
+		/// </value>
+		public override string Comment {
+			get { return GetLangAltNode (DC_NS, "description"); }
+			set { SetLangAltNode (DC_NS, "description", value); }
+		}
+		
+		/// <summary>
+		///    Gets or sets the keywords for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string[]" /> containing the keywords of the
+		///    current instace.
+		/// </value>
+		public override string[] Keywords {
+			get { return GetCollectionNode (DC_NS, "subject") ?? new string [] {}; }
+			set { SetCollectionNode (DC_NS, "subject", value, XmpNodeType.Bag); }
+		}
+		
+		/// <summary>
+		///    Gets or sets the rating for the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> containing the rating of the
+		///    current instace.
+		/// </value>
+		public override uint? Rating {
+			get {
+				uint val;
+				
+				if (UInt32.TryParse (GetTextNode (XAP_NS, "Rating"), out val))
+					return val;
+				
+				return null;
+			}
+			set {
+				SetTextNode (XAP_NS, "Rating", value != null ? value.ToString () : null);
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the time when the image, the current instance
+		///    belongs to, was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the time the image was taken.
+		/// </value>
+		public override DateTime? DateTime {
+			get {
+				// TODO: use correct parsing
+				try {
+					return System.DateTime.Parse (GetTextNode (XAP_NS, "CreateDate"));
+				} catch {}
+				
+				return null;
+			}
+			set {
+				// TODO: write correct format
+				SetTextNode (XAP_NS, "CreateDate", value != null ? value.ToString () : null);
+			}
+		}
+		
+		/// <summary>
+		///    Gets or sets the orientation of the image described
+		///    by the current instance.
+		/// </summary>
+		/// <value>
+		///    A <see cref="TagLib.Image.ImageOrientation" /> containing the orientation of the
+		///    image
+		/// </value>
+		public override ImageOrientation Orientation {
+			get { return 0; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the software the image, the current instance
+		///    belongs to, was created with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> containing the name of the
+		///    software the current instace was created with.
+		/// </value>
+		public override string Software {
+			get { return GetTextNode (XAP_NS, "CreatorTool"); }
+			set { SetTextNode (XAP_NS, "CreatorTool", value); }
+		}
+		
+		/// <summary>
+		///    Gets or sets the latitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the latitude ranging from -90.0
+		///    to +90.0 degrees.
+		/// </value>
+		public override double? Latitude {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the longitude of the GPS coordinate the current
+		///    image was taken.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the longitude ranging from -180.0 
+		///    to +180.0 degrees.
+		/// </value>
+		public override double? Longitude {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets or sets the altitude of the GPS coordinate the current
+		///    image was taken. The unit is meter.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the altitude. A positive value
+		///    is above sea level, a negative one below sea level. The unit is meter.
+		/// </value>
+		public override double? Altitude {
+			get { return null; }
+			set {}
+		}
+		
+		/// <summary>
+		///    Gets the exposure time the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the exposure time in seconds.
+		/// </value>
+		public override double? ExposureTime {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the FNumber the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the FNumber.
+		/// </value>
+		public override double? FNumber {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the ISO speed the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the ISO speed as defined in ISO 12232.
+		/// </value>
+		public override uint? ISOSpeedRatings {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the focal length the image, the current instance belongs
+		///    to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="System.Nullable"/> with the focal length in millimeters.
+		/// </value>
+		public override double? FocalLength {
+			get { return null; }
+		}
+		
+		/// <summary>
+		///    Gets the manufacture of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the manufacture name.
+		/// </value>
+		public override string Make {
+			get { return GetTextNode (TIFF_NS, "Make"); }
+		}
+		
+		/// <summary>
+		///    Gets the model name of the recording equipment the image, the
+		///    current instance belongs to, was taken with.
+		/// </summary>
+		/// <value>
+		///    A <see cref="string" /> with the model name.
+		/// </value>
+		public override string Model {
+			get { return GetTextNode (TIFF_NS, "Model"); }
+		}
+		
+#endregion
+	}
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]