[f-spot/taglib-metadata] Taglib# sync e78bc6160d12b957134fc9274d0e3852f5e2e560
- From: Ruben Vermeersch <rubenv src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [f-spot/taglib-metadata] Taglib# sync e78bc6160d12b957134fc9274d0e3852f5e2e560
- Date: Sun, 16 May 2010 07:38:30 +0000 (UTC)
commit 31f00bc46a12bd7f69d7ddd75adced503b5cbb7a
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 & ~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 & ~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 <System.dll>
+ /// #using <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 > 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 & ~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 <System.dll>
+ /// #using <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<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 <System.dll>
+ /// #using <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<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<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 <System.dll>
+ /// #using <System.Xml.dll>
+ /// #using <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 < 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 & ~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 & ~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 & ~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 <System.dll>
+ /// #using <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 & ~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]